Login
Cerca all'interno di JavaPortal
Help
Home Page Documentazione Forum Progetti Partner Pubblica!
Documentazione > Tutorial > EJB 2.0: Relazione tra entity Bean gestita dal container
Hide
Best Practices
EJB
Frameworks
Howto
J2EE
J2ME and Wireless
J2SE
JSP e Servlet
Java Application Server
Java IDE/Tools
Java Media
Java Security
Java Sys Admin
Java e XML
Java e SQL
OpenSource Java
Patterns
Repository
Tesi
UML
Web Services
Slide
White Paper di jws.it
project management
Eventi
Groovy

Hai una tesi in Java?
Tesine preparate
per esami?
Pubblica tutto su
JavaPortal!

Scrivi al nostro staff


Project Kenai


Henry Ford
Vero progresso quando i vantaggi di una nuova tecnologia diventano per tutti


GRASP : Pattern Low Coupling


Rss Feed
Home Page
Articoli
News
Forum
Classi

  Visualizza Commenti (0) Aggiungi Commento    
Add to Shortcuts
 
Vota l'articolo
EJB 2.0: Relazione tra entity Bean gestita dal container
By Simone Panepuccia
10 giugno 2005
Valutazione Acquisita: 50

  EJB 2.0: Relazione tra entity Bean gestita dal container

Le specifiche EJB 2.0 hanno apportato novità rilevanti per ciò che riguarda la gestione della persistenza da parte del container (CMP). In particolare, l'utilizzo delle nuove interfacce locali (home e a componenti) permette di implementare gli oggetti dipendenti con entity bean.
In questo esempio vedremo come utilizzare CMR (Container Managed Relationship) per creare una semplice struttura di oggetti persistenti.
Immaginiamo di dover implementare un sistema di gestione acquisti in cui un utente possa registrarsi, entrare e aggiungere un ordine di acquisto alla sua lista. Possiamo pensare all'utente come ad un oggetto persistente realizzabile con un entity bean che a sua volta si relaziona con un altro oggetto persistente che rappresenta un ordine di acquisto. Ovviamente ogni utente potrà avere più ordini di acquisto, mentre un ordine potrà appartenere ad un solo utente.
Una semplice classe client si occuperà della logica operativa di registrazione, login ed aggiunta ordini chiamando i metodi esposti dall'interfaccia a componenti remota dell'entity bean che rappresenta l'utente e che chiameremo 'User'.

Fig. 1  EJB 2.0: Relazione tra entity Bean gestita dal container

Notare come l'entity bean Order sia legato solamente a User. I metodi di gestione degli ordini saranno esposti al Client da User e Client non avrà mai a che fare direttamente con Order.
Questo è importante non solo per motivi architetturali, ma anche perchè se decidiamo di utilizzare un entity bean per rappresentare un Order, questo entity sarà di tipo 'local' in quanto l'accesso ai dati che esso rappresenta potrà essere gestita nell'ambito dello stesso processo senza chiamate remote (nella stessa Java Virtual Machine, nella fattispecie) da parte del bean User. Questo comporta il fatto che non si possano esporre, nell'interfaccia remota di User, metodi che ritornano riferimenti ad interfacce locali.
Iniziamo a vedere le interfacce relative a UserEJB:

UserHome.java

package user;

import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

      public interface UserHome extends EJBHome {

     public User create(String userName, String password, String name, String surname,String address, int                                                       age, String sex, String title)  throws CreateException, RemoteException;

   public User findByPrimaryKey(String userName) throws CreateException, FinderException, RemoteException;
}


Come si può notare dagli argomenti passati ai metodi di creazione e ricerca uno User è definito da campi informativi (name, surname, ecc.), da un campo 'password' e da un campo 'userName' che fa da chiave primaria (e infatti non possono esistere due utenti con lo stesso user name).

User.java (interfaccia a componenti remota)

package user;

import javax.ejb.EJBObject;
import java.rmi.RemoteException;
import java.util.Hashtable;
import java.util.Vector;
import javax.ejb.*;

public interface User extends EJBObject {

    public Hashtable viewUserInfo() throws RemoteException;
       public String getPasswordValue() throws RemoteException;
       public Vector viewOrderList() throws RemoteException;
       public void addOrderToList(String orderId, int amount, String desc) throws CreateException,                                                                   RemoteException;
}


Questa interfaccia espone al client i metodi che contengono logica operativa del bean. Quindi avremo un metodo per ottenere i dati di un utente ( viewUserInfo()), uno per ottenere la lista degli ordini relativi all'utente, uno per aggiungere un ordine alla lista ed, infine, un metodo che ritorna la password dell'utente. Quest'ultimo metodo serve per effettuare il controllo dell'accesso in fase di login. Indubbiamente non si tratta di una policy molto sicura, in quanto si espone ingenuamente un metodo in remoto che restituisce una password al solo scopo di effettuare un controllo. Sarebbe più sicuro spostare questo controllo direttamente sul bean ed esporre un metodo che restituisca un boolean dopo aver controllato la password che gli viene passata per argomento, ma per gli scopi di questo esempio questo non è necessario.

Andiamo a vedere cosa deve fare una classe client che vuole utilizzare il nostro entity bean 'UserBean' (il codice implementativo del bean lo vedremo tra poco): Client.java

Client è una classe di test che viene eseguita da console e che prende diversi argomenti ed opzioni a seconda dell'operazione che si vuole svolgere col bean. Il primo argomento è un'opzione che rappresenta il tipo di operazione, i seguenti sono gli argomenti veri e propri:

-l login args: username password
-r registrazione args: username password name surname address age sex(M,F) jobtitle
-o aggiungi ordine args: username password numeroOrdine quantità descrizione

Così per registrare un nuovo utente dovremo usare:
java Client -r pippo pluto Mario Rossi "Via dei Matti, 0" 35 M macellaio

Lo stesso utente per effettuare un login dovrà usare:
java Client -l pippo pluto

Mentre per aggiungere un ordine alla propria lista:
java Client -o pippo pluto 1 3 "Quarti di bue"

Vediamo che il metodo main() effettua prima un controllo di validità sugli argomenti passati e poi, solo in caso di argomenti corretti, provvede ad istanziare l'oggetto Client che, nel suo costruttore, effettua il lookup su jndi dell'interfaccia Home di User con la quale poi si potranno creare nuovi User o cercarne uno già esistente. Il lookup jndi può essere un'operazione costosa in termini di prestazioni e, quindi, è meglio evitare di eseguirla inutilmente (come nel caso di parametri errati).

Il metodo 'findUser()' viene chiamato in seguito alla ricezione di un parametro '-l' (login) ed ottiene un riferimento all'interfaccia a componenti 'User'. Se l'utente esiste viene effettuato il controllo della password ed, in caso di esito positivo, viene stampato un messaggio di benvenuto ed un riepilogo dei dati dell'utente, nonchè la sua lista di ordini.

Quando main() riceve '-r' si preoccupa di chiamare il metodo 'createUser()' che, similmente a 'findUser()', utilizza la Home interface di UserBean per restituire un riferimento all'interfaccia a componenti 'User', questa volta di un entity bean appena creato. Da notare come non sia possibile, con jdk 1.3, catturare una nested exception in maniera pulita (e cioè senza andare a leggere la stringa di messaggio dell'eccezione). Questo problema si presenta in caso di tentativo di duplicazione di una chiave primaria (es. un utente si vuole registrare con uno userName già esistente): il metodo create() lancia un'eccezione 'RemoteException' con annidata una 'DuplicateKeyException'. Per capire se effettivamente si tratta di una eccezione generata da un tentativo di duplicazione dovremmo andare a guardare la nested exception e, in tal caso, stampare un messaggio che informa l'aspirante utente di scegliersi un altro userName. Questo è possibile con jdk 1.4 grazie al metodo getCause() da chiamare sull'eccezione remota. Nel codice in esempio diamo per scontato che una RemoteException lanciata in fase di creazione del bean sia sempre dovuta ad una DuplicateKeyException. Ovviamente non datelo per scontato su un progetto di lavoro!

Nel caso di parametro '-o' il Client provvederà ad istanziare il riferimento remoto al bean, chiamando findUser() e, successivamente, aggiungerà l'ordine sfruttando il metodo addOrderToList() esposto da User.

Vediamo ora le implementazioni dei due entity bean UserBean.java ed OrderBean.java:

I metodi di callback vengono lasciati vuoti perchè saranno implementati dal container. Nel caso di un entity BMP avremmo dovuto preoccuparci anche di questo aspetto. Una delle grosse differenze con EJB 1.1 è che, con le nuove specifiche, i campi persistenti del bean vengono indicati solamente tramite metodi di accesso (secondo lo standard JavaBean: getXxx, setXxx con xxx proprietà) ASTRATTI. In questo contesto noi forniamo una classe astratta al container il quale si preoccuperà di implementare i metodi relativi alla gestione dei campi persistenti del bean.
A questo punto rimangono solamente i metodi operativi ed è su questi che deve concentrarsi lo sviluppatore di EJB CMP.

Il metodo viewUserInfo() restituisce semplicemente una Hashtable contenente i dati dell'utente.
Il metodo getPasswordValue(), come già detto, non brilla per sicurezza ma è utile a scopo didattico per mostrare il fatto che è consigliabile filtrare al client i metodi di accesso ai campi persistenti del bean perchè non si può aggiungere logica operativa su questi metodi in quanto essi vengono implementati dal container. Utilizzare getPasswordValue() anzichè esporre getPassword nella remote interface User ci consente di eseguire della logica operativa prima di restituire il dato richiesto. Ad esempio si potrebbe implementare un contatore di accessi a getPassword() per migliorare la sicurezza e bloccare l'utente dopo un certo numero di accessi falliti...

Fra i metodi relativi ai campi persistenti c'è una coppia che si riferisce a Collection di oggetti Orders (getOrders, setOrders). Bene, questo significa che un oggetto dipendente viene gestito come fosse un campo persistente qualunque. In questo caso ci riferiamo a Collection di oggetti perchè la cardinalità fra User e Order è 1..n (uno a molti). Il container CMR si occuperà di gestire le relazioni fra i due bean, mentre lo sviluppatore avrà a che fare semplicemente con delle Collection.
Quando il client chiede di aggiungere un ordine all'utente, il metodo addOrderToList chiederà al container la Collection di oggetti Order, creerà un nuovo oggetto Order tramite la create di OrderHome e lo aggiungerà alla Collection.
Un oggetto Order, come abbiamo intravisto all'inizio, è composto da una chiave primaria (idOrder), una quantià (amount) ed una descrizione (desc).
Ecco dunque l'interfaccia Home locale di OrderBean:

OrderHome.java

package user;

import javax.ejb.EJBLocalHome;
import javax.ejb.CreateException;
import javax.ejb.FinderException;

public interface OrderHome extends EJBLocalHome {

public Order create(String orderId, int amount, String desc) throws CreateException;

public Order findByPrimaryKey(String orderId) throws CreateException, FinderException;


Il metodo viewOrderList() di UserBean restituisce, quindi, una struttura dati composta da un Vector di Hashtables, ognuna contenente i dati di un ordine.
È bene ricordare che i metodi che vengono chiamati tramite RMI possono restituire solamente tipi primitivi od oggetti che implementano Serializable

Order.java

package user;

import javax.ejb.*;


public interface Order extends EJBLocalObject {

                    public int getAmountValue();
                    public void setAmountValue(int amount);
                    public String getDescriptionValue();
                    public void setDescriptionValue(String desc);

                    public String getOrderId();
                    public void setOrderId(String orderId);

                    public User getUser();
                    public void setUser(User userItem);
}


L'interfaccia a componenti locale Order espone semplicemente i metodi di accesso ai campi persistenti di OrderBean (in realtà sono dei metodi che al loro interno chiamano i veri metodi d'accesso, come visto prima) e all'oggetto User che sta in relazione 1..1 (uno ad uno) con l'Order in questione.
OrderBean.java

L'implementazione di OrderBean è veramente semplice e, l'unica cosa da notare, è l'utilità dei metodi di filtro setAmountValue() e setDescriptionValue(). Se avessimo esposto nell'interfaccia a componenti Order direttamente setAmount() e setDesc() non avremmo potuto effettuare nessun controllo sulla validità degli argomenti poichè i due metodi d'accesso sono, come già detto, implementati dal container.

Vediamo, ora, come fare il deploy della nostra applicazione e come farla girare su Bea Weblogic 7.
Innanzitutto creiamo una cartella di lavoro che chiameremo userApp. In questa cartella creiamo la struttura di un'applicazione EJB, quindi ci serve la cartella META-INF ed una cartella user.
Sotto user andremo a mettere il codice relativo agli entity bean: UserBean.java, User.java, UserHome.java, OrderBean.java, Order.java, OrderHome.java.
Sotto META-INF dovremo salvare i files xml descrittori di deployment che seguono:ejb-jar.xml

Il file ejb-jar.xml definisce la struttura del bean e le sue interfacce, i campi persistenti e la chiave primaria. Notare che i due bean implementano interfacce di tipo diverso: UserBean di tipo remoto, OrderBean di tipo locale come abbiamo già visto nel codice. Particolarmente importante è la definizione delle relazioni fra le due entità. Fra i tag <relationships> viene precisato il rapporto che intercorre fra i due bean. In particolare il tag <multiplicity> definisce la cardinalità (zero a molti fra User e Order, uno a uno fra Order e User). Il tag <cascade-delete/> indica al container di effettuare la cancellazione degli oggetti dipendenti (Order) collegati ad oggetti persistenti che vengono cancellati. Ad esempio se cancello uno User, il container provvederà a cancellare anche tutti i suoi Order correlati. In questo esempio, come avrete notato, non abbiamo sviluppato codice riguardante la modifica o la cancellazione dei dati per semplicità.
weblogic-ejb-jar.xml

Con questo file si stabilisce che il container dovrà riferirsi al file META-INF/weblogic-cmp-rdbms-jar.xml per la gestione della persistenza tramite DB. Molto importante è la definizione del jndi name dell'interfaccia Home dei bean, nome al quale dobbiamo riferirci nel codice per effettuare il lookup della risorsa.
weblogic-cmp-rdbms-jar.xml

Questo è il file che definisce la relazione fra i campi persistenti del bean ed il database che ne garantisce effettivamente la persistenza. Si può notare che qui viene indicata la tabella che mappa il bean e le colonne che corrispondono ai campi persistenti. Viene anche indicato il tipo di relazione fra le due entità riferendosi ad essa per nome: 'User-Order', infatti è il nome dato all'elemento <ejb-relation> nel descrittore di deployment ejb-jar.xml.
La penultima riga indica al container che dovrà occuparsi anche della creazione della tabella. In un esempio come questo possiamo anche affidarci al container per la definizione delle tabelle (a proposito, usiamo il DB Pointbase integrato in Weblogic ed il datasource degli esempi di Bea, come indicato nel tag <data-source-name>), ma se vogliamo avere pieno controllo sul DB dobbiamo porre il tag <create-default-dbms-tables> a false, creare il database, creare un nuovo data source dalla console di Weblogic ed assegnargli un jndi name e creare manualmente le tabelle con cui vogliamo realizzare la persistenza dei nostri entity bean. Di seguito lo script di creazione delle due tabelle per Oracle, MySQL e PostgreSQL:

-- oracle DDL

DROP TABLE TORDER;

CREATE TABLE TUSER(
USERNAME VARCHAR2(20) NOT NULL CONSTRAINT PK_TUSER1 PRIMARY KEY,
PASSWORD VARCHAR2(20) NOT NULL,
NAME VARCHAR2(30) NOT NULL,
SURNAME VARCHAR2(30) NOT NULL,
ADDRESS VARCHAR2(40),
AGE INTEGER,
SEX CHAR(1),
TITLE VARCHAR2(30));


DROP TABLE TORDER;

CREATE TABLE TORDER(
ID_ORDER VARCHAR2(10) NOT NULL CONSTRAINT PK_TORDER1 PRIMARY KEY,
AMOUNT INTEGER DEFAULT 1,
DESC VARCHAR2(40),
USERNAME VARCHAR2(20),
USERNAME_1 VARCHAR2(20) NOT NULL);


ALTER TABLE TORDER
ADD CONSTRAINT FK_TORDER_1
FOREIGN KEY (USERNAME_1) REFERENCES TUSER (USERNAME);

-- MySQL DDL

DROP TABLE TUSER;

CREATE TABLE TUSER(
USERNAME VARCHAR(20) NOT NULL,
PASSWORD VARCHAR(20) NOT NULL,
NAME VARCHAR(30) NOT NULL,
SURNAME VARCHAR(30) NOT NULL,
ADDRESS VARCHAR(40),
AGE INT,
SEX CHAR(1),
TITLE VARCHAR(30),
PRIMARY KEY (USERNAME));


DROP TABLE TORDER;

CREATE TABLE TORDER(
ID_ORDER VARCHAR(10) NOT NULL,
AMOUNT INT DEFAULT 1,
DESC VARCHAR(40),
USERNAME VARCHAR(20),
USERNAME_1 VARCHAR(20) NOT NULL,
FOREIGN KEY (USERNAME_1) REFERENCES TUSER (USERNAME),
PRIMARY KEY (ID_ORDER));

-- PostgreSQL DDL

DROP TABLE TUSER;

CREATE TABLE TUSER(
USERNAME varchar(20) NOT NULL PRIMARY KEY,
PASSWORD varchar(20) NOT NULL,
NAME varchar(30) NOT NULL,
SURNAME varchar(30) NOT NULL,
ADDRESS varchar(40),
AGE int4,
SEX char(1),
TITLE varchar(30));

DROP TABLE TORDER;

CREATE TABLE TORDER(
ID_ORDER varchar(10) NOT NULL PRIMARY KEY,
AMOUNT int4 DEFAULT 1,
DESC varchar(40),
USERNAME varchar(20) NOT NULL,
FOREIGN KEY (USERNAME) REFERENCES TUSER (USERNAME));

Le due tabelle che realizzano la persistenza dei bean su DB possono essere così schematizzate:

 

Fig. 2 EJB 2.0: Relazione tra entity Bean gestita dal container 

Una volta realizzata la mappatura degli oggetti persistenti su DB, dobbiamo effettuare il deploy dell'applicazione. I passi da seguire sono gli stessi di qualsiasi altra applicazione EJB, e cioè:


1.Creare un archivio JAR contenente la struttura server dell'applicazione che rappresenta il Naked EJB:
javac user/*.java
jar cvf UserBeanNaked.jar META-INF/*.* user/*.class

2.Creare l'EJB deployable:
java weblogic.ejbc UserBeanNaked.jar UserBeanDeployable.jar
Avendo cura di rendere visibile nel CLASSPATH il package weblogic che si trova in WL _HOME/weblogic700/server/lib

3.Deployare l'applicazione avviando il server degli esempi nella cartella WL_HOME/weblogic700/samples/server/config/examples con il comando startExamplesServer.cmd (Windows) oppure sh startExamplesServer.sh (Unix/Linux). Per avviare la console di Weblogic aprite un browser all'indirizzo: http://localhost:7001/console e clickate, nell'albero di sinistra su EJB. Vi verrà proposto di configurare un nuovo EJB. Fate l'upload del vostro UserBeanDeployable.jar e quando comparirà nella lista degli esempi selezionatelo e clickate su Deploy.

4.Ora dovete solo compilare la classe Client.java facendo attenzione alla visibilità delle interfacce User e UserHome, le quali devono poter essere importate da Client.

5.Testate l'applicazione eseguendo una registrazione come già spiegato nella parte relativa al client.

7.Verificate la registrazione effettuando un login o l'aggiunta di un ordine.

 Attachments List
Generic DocumentClient Java
Generic Documentweblogic-cmp-rdbms-jar.xml
Generic DocumentOrderBean Java
Generic Documentweblogic-ejb-jar-xml
Generic Documentejb-jar-xml.xlm
Generic DocumentUserbean Java
Username:
Password:
To sign up for an account, click register... Register
Hide





Powered By



Campagna Anti-IF


Skin


PARTNER
Zio Budda
HostingJava


LICENZA



Eccetto dove diversamente specificato, i contenuti di questo sito sono rilasciati sotto licenza Creative Commons

Sitemap  © 2002-2004 Copyright Information. Privacy . Today is domenica 1 agosto 2010