Inizia una nuova parte della serie dedicata alla progettazione di applicazioni EJB: in questo e nei prossimi articoli parleremo di quali siano le indicazioni da seguire per la realizzazione dello strato di mapping OO-RDB basato su Entity beans CMP
Obiettivo della serie
Negli articoli precedenti di questa serie sono state presentate alcune tecniche di progettazione
utilizzate nell‘ambito di applicazioni Java EE basate su tecnologia web e EJB; è stata ANCHE
l‘occasione per presentare alcuni dei patten più importanti sia per l‘organizzazione e
interconnessione dei vari strati applicativi (es business delegate in accoppiamento con session
facade) sia per lo scambio delle informazioni fra gli strati e relativa copia in memoria (pattern DTO
e VO).
Nella ultima puntata della serie dedicata ai meccanismi di migrazione dei dati fra gli strati
applicativi, si era detto che lo strato di presentation layer (tipicamente una applicazione web)
interagisce con quello di business logic (dove i dati vengono non solo ricavati dal layer di
persistenza, ma anche manipolati secondo il workflow della applicazione) dal quale ricava le
informazioni necessarie per la visualizzazione all‘utente e viceversa.
Si era anche visto come lo scambio di informazioni debba essere eseguito per mezzo di appositi oggetti
di trasporto detti Data Transfer Object, oggetti con il compito di contenere al loro interno le
informazioni per il trasferimento delle stesse dallo strato mittente (es lo strato web nel caso si
voglia inviare i dati per una modifica) verso il destinatario dove verranno consumate.
Questo modo di concepire una applicazione (ovvero seguendo i flussi dati) sarà una delle chiavi di
lettura adottate in questa serie di articoli.
Tutte le considerazioni fatte in precedenza in definitiva riguardano gli aspetti relativi alla
comunicazione fra i vari strati applicativi, la modalità con la quale i dati debbano essere manipolati
e manenuti sul presentation layer: niente (o quasi) per adesso è stato detto circa quello che si
nasconde “dietro” il Session Faà§ade (del resto questo è un risultato cercato e ottenuto grazie alla
motivazione di questo pattern).
Rimane da capire come sia possibile trasferire i dati contenuti all‘interno del DTO da e verso il
sistema di persistenza (tipicamente il database relazionale sul quale tutta l‘applicazione si poggia).
Utilizzando una terminologia Java Enterprise, quello che ancora non si è detto è come utilizzare con
profitto un framework di mapping Object-Relational al fine di nascondere tutti i dettagli relativi
alla gestione della connessione con il database.
Si vedrà anche come sia possibile gestire in maniera trasparente la trasformazione oggetto-tabella con
particolare riferimento alle relazioni e aggregazioni di oggetti, cancellazioni a cascata e simili.
NOTA: la specifica Java EE impone che l‘applicazione in esecuzione all‘interno del container non debba
e non possa gestire direttamente la connessione al database, essendo questo un compito a totale carico
dell‘application server. Questo, dopo aver instaurato una connessione fisica con il server dati, mette
a disposizione una connessione virtuale con il supporto di un connection pooling, sicurezza,
transazionalità e tutti i servizi tipici di una applicazione di alto livello.
Al programmatore resta da scegliere lo strumento tramite il quale effettuare il mapping Object-
Relational (OO-RDB) ed eseguire tramite tale strumento la sincronizzazione dello stato degli oggetti
con i dati residenti nel database.
L‘evoluzione tecnologica degli ultimi anni ha ampiamente giustificato e anzi fortemente consigliato
l‘utilizzo di strumenti di mapping Object relational per sincronizzare lo stato di un oggetto. Lo
stato dell‘arte attuale vede lo scenario dominato da framework e tecnologie quali Hibernate, JDO, CMP
2.0 (con EJB 3.0 + Hibernate in fase di rilascio).
Quanto vedremo non deve essere considerata come l‘unica soluzione possibile, dato che nella realtà dei
progetti enterprise sempre più spesso si vedono mescolare con successo tecnologie differenti in modo
da rispondere alle varie esigenze e requisiti del progetto. Tanto per fare un esempio in questo
particolare momento è molto in voga per la realizzazione di applicazioni enterprise, l‘utilizzo di
session beans per la parte di business unitamente ad Hibernate per la parte di mapping.
Parlando invece di applicazioni “solo” EJB (oggetto di questa serie di articoli) lo strumento che
andremo a vedere è il Container Managed Persistence, tecnologia giunta ormai alla versione 2.0.
In realtà CMP 2.0 non è altro che un pezzetto della specifica EJB, ma a causa della importanza della
tecnologia e la diversità rispetto alle versioni precedenti (che non venivano considerate come una
parte a se stante ma integrate a tutti gli effetti in EJB 1.0, 1.1), spesso viene indicata da sola
per rafforzarne l‘importanza.
Nota importante sulle motivazioni della serie
Questo nuovo gruppo di articoli dedicati alla persistenza con CMP, e facenti parte della serie sullo
sviluppo di applicazioni enterprise con EJB, non è stato pensato per essere un corso teorico su EJB ne
sui trucchi di CMP.
Lo scopo è piuttosto quello di fornire un approccio concreto per utilizzare alcune delle tecniche di
progettazione più in voga attualmente; come fatto nei precedenti articoli dedicati allo strato
session, si cercherà di mostrare come usare pattern e tecniche di progettazione al fine di progettare
e implementare lo strato di persistenza cercando di perseguire gli obiettivi mostrati in precedenza,
ovvero flessibilità , scalabilità , prestazioni, semplicità di manutenzione.
Non si seguirà una trattazione rigorosa e meticolosa su cosa sia un Enterprise Java Beans CMP, ne
scenderemo nel dettaglio di aspetti come il ciclo di vita di un entity CMP o sulla miriade di
messaggi che il bean e il container si scambiano durante tutto il corso della vita di un componente
come questo.
Per questo genere di argomenti si può fare riferimento a un qualsiasi manuale su EJB (come ad esempio
[1]) compresa la serie di articoli pubblicata in passato su MokaByte confluita infine nel capitolo su
Enterprise Java Beans del Manuale Pratico di Java, realizzato dalla redazione di MokaByte (vedi [2]).
Cosa è un entity bean
Un bean entity è un componente EJB il cui scopo principale è quello di rappresentare una entità
astratta, come insegna comunemente un qualsiasi processo di analisi a oggetti. Un oggetto di questo
tipo rappresenta quindi un dato semplice o un aggregato, il quale possiede attributi di stato e di
relazione (con altri oggetti/entità ).
Importante tenere sempre ben presente che un CMP vive all‘interno di un application server il quale
mette a disposizione alcuni servizi: il più importante è certamente il meccanismo di persistenza dello
stato dell‘oggetto, ma non si devono trascurare la transazionalità , la gestione della sicurezza, la
gestione del ciclo di vita delle varie istanze di oggetti gestiti in modalità pooled.
Tralasciando il punto di vista dell‘application server e ponendosi invece dal punto di vista del
business logic layer (che sarà l‘utilizzatore dei componenti EJB) si potrebbe dare una definizione
molto sintetica e pragmatica: un CMP è un componente che mappa una entità astratta che potrà essere
utilizzata nel workflow applicativo essendo sicuri che l‘application server svolgerà per noi una serie
di operazioni.
Non ci si preoccuperà quindi di transazionalità , sicurezza, resistenza al carico di lavoro (grazie al
pooling di istanze) e persistenza: con la consapevolezza che l‘application server svolge queste queste
mansioni, si concentrerà l‘attenzione su quelle tecniche e pattern architetturali che possano
soddisfare le necessità del business layer, senza ostacolare il lavoro del container ma anzi
facilitandone il compito.
Prima regola: stato sì, comportamento no
La programmazione ad oggetti ci ha da sempre insegnato che unitamente allo stato, un oggetto possiede
anche un comportamento (reso possibile grazie all‘uso dei metodi), cosa che lo distingue da strutture
dati tipiche dei linguaggi di programmazione non ad oggetti.
In una applicazione multistrato di fascia enterprise, sebbene la specifica non lo imponga e nemmeno lo
preveda ufficialmente, è buona norma adottare una regola universale che porta a una sostanziale
differenza fra un oggetto comune e un entity bean: essendo questo un elemento che rappresenta una
entità astratta, dovrebbe sempre memorizzare uno stato e non offrire nessuna logica di business.
Una entità quindi non deve contenere metodi di business logic ma eventualmente metodi che consentano
di gestire lo stato della entità stessa: si dovrebbero quindi inserire metodi accessori/modificatori
(getXXX e setXXX) o metodi che compiano una qualche conversione dei dati stessi, possibilmente senza
alterare lo stato del bean. Ad esempio se il bean contiene una variabile di tipo data che memorizza la
data di nascita di una persona, potrebbe essere utile disporre di un metodo che restituisca tale data
in formato stringa o che esegua una qualche conversione da data in formato europeo a formato
statunitense.
Prendendo ad esempio il caso introdotto più avanti nell‘articolo, se si considera una entità
rappresentante un conto in banca o un bancomat, è lecito che sia possibile interrogare il conto per
conoscere il saldo, ma è certamente poco piacevole che il conto corrente eseguire operazioni
autonomamente senza il controllo di un soggetto attivo (il cassiere o genericamente la business logic
controllata dal sistema web di home banking).
Solo interfaccia locale
Altra regole molto da seguire nella progettazione di una applicazione multistrato è quella di non
implementare mai l‘interfaccia remota di un entity bean (ovvero permettere ad un client di accedere
direttamente all‘entity senza l‘intercessione dello strato di logica): se un entity rappresenta una
struttura dati astratta che mappa in un qualche modo una entità del data model, risulta semplice
comprendere quanto sia controindicato fornire diretto accesso alla applicazione client a tale entità .
Per una moltitudine di ragioni è più saggio e corretto fornire accesso al data model tramite
l‘apposito strato di business logic dove si potranno concentrare tutti controlli su sicurezza,
coerenza sui dati, e procedere secondo le regole imposte dal workflow applicativo.
Con un esempio un po‘ pittoresco, si potrebbe pensare al caso in cui si debba effettuare un pagamento
presso un negoziante di fiducia fornendo direttamente il bancomat con tanto di codice, piuttosto che
usare il bancomat per eseguire il pagamento tramite POS. In questo caso il cliente del negozio svolge
il compito di session faà§ade agli occhi del negoziante: è lui che conosce il codice del bancomat e lo
tiene segreto (session-security), ed è lui che sfrutta la logica di business per accedere in maniera
controllata al datamodel (il conto corrente).
La creazione di un bean
Come dovrebbe essere noto, non è possibile creare direttamente un entity bean: come molti altri
aspetti relativi al ciclo di vita del componente, anche la procedura di creazione dell‘oggetto viene
gestita dal container, il quale in un determinato momento del ciclo di vita del bean ne invoca il
metodo ejbCreate() e ejbPostCreate().
Supponendo il caso in cui si debba procedere alla creazione di un bean con i dati inserite
dall‘utente tramite un form HTML: tali dati fluiscono tramite un DTO dallo strato web, passano per
quello di business logic per arrivare all‘entity bean in questione. Il DTO potrebbe contenere
solamente le informazioni necessarie alla creazione della entità che si deve aggiungere al sistema,
oppure anche dati (chiavi o DTO completi) relative a entità con le quali il nuovo oggetto dovrà
instaurare relazioni di vario genere. Questi concetti sono stati ampiamenti analizzati negli articoli
in cui si è parlato delle possibili tecniche di gestione dei DTO (vedi [3] e [4]).
Il metodo ejbCreate() è quindi il luogo deputato per eseguire il trasferimento dei dati dal DTO al
bean.
Per rendere più chiara la trattazione è utile prendere un esempio concreto per spiegare i vari
passaggi; al solito l‘applicazione di riferimento è MokaByteCMS, il CMS open source sviluppato dal
team di sviluppatori di MokaByte.
Si immagini quindi che si debba procedere nella creazione di un articolo da inserire nel CMS.
Per tale scopo il session bean ArticleManager (avente funzione di session faà§ade) espone tramite la
sua interfaccia remota il seguente metodo
public void createArticle(ArticleDTO articleDTO) throws XXXXXXXXXX;
Tale metodo viene in genere invocato all‘interno di una classe dello strato web della applicazione
(tipicamente una action MVC). Ad esempio si potrebbe sintetizzare con le seguenti operazioni la
sequenza delle operazioni svolte all‘interno del client
// inizializza la connessione con la interfaccia remota del session faà§adeString FACADE_NAME = "ArticleManagerSF";Class FACADE_CLASS = com.mokabyte.cms.ejb.ArticleManagerSFHome.class;ArticleManagerSFHome articleManagerSFHome;try {ServiceLocator locator = ServiceLocator.getInstance(jndiProperties);articleManagerSFHome = (ArticleManagerSFHome) locator.getEjbHome(FACADE_NAME, FACADE_CLASS);if (articleManagerSFHome == null) {throw new Exception("Impossibile ricavare la home interface " + FACADE_NAME);}
// ricava l‘interfaccia remota del session faà§adearticleManagerSFRemote = articleManagerSFHome.create();} catch (ServiceLocatorException e) {throw new Exception(e.getMessage());}. . .try {logger.debug("invocazione di SF remoto");// invoca il metodo remoto per la creazione dell‘articoloarticleManagerSFRemote.createArticle(articleDTO);} catch (RemoteException re) {String msg = "Errore di invocazione remota sul metodo di faà§ade createArticle() " + re;logger.error(msg);throw new FacadeInvocationException(msg);}
Il codice di esempio appena riportato è stato così riassunto per esigenze di sintesi e chiarezza, ma
nella realtà è eseguito dal client non direttamente ma tramite gli ormai noti pattern Business
Delegate e Service Locator.
Passando al session faà§ade, all‘interno del metodo createArticle() si delega la creazione del bean al
bean stesso invocandone il metodo create(), il quale riceve il DTO ricevuto dalla strato client.
Per fare questo il session bean dovrà aver precedentemente ricavato l‘interfaccia home locale del bean
Article: questa operazione in genere viene effettuata all‘interno del metodo setSessionContext() che
rappresenta il punto di ingresso del ciclo di vita di un session:
public void setSessionContext(SessionContext sessionContext) {this.sessionContext = sessionContext;try {// inizializza le home interfacesif (articleLocalHome == null) {try {ServiceLocator locator = ServiceLocator.getInstance();// ricava la home interface con l‘ausilio del service locatorarticleLocalHome = (ArticleLocalHome) locator.getEjbLocalHome (ENTITY_NAME);} catch (ServiceLocatorException e) {throw new EJBException(e.getMessage());}}} catch (Exception e) {throw new EJBException(e.getMessage());}}
A questo punto può essere invocato il metodo createArticle() che al suo interno potrebbe essere così
organizzato:
public void createArticle(ArticleDTO articleDTO) throws EJBException {try {// crea il bean invocando il metodo create sulla sua Home interfaceArticleLocal articleLocal = articleLocalHome.create(articleDTO);}catch (Exception e) {throw new EJBException(e.getMessage());}// Esegue altre operazioni di sincronizzazione dello stato. . . }
Si noti la presenza di ulteriori operazioni relative alla creazione di un articolo: ad esempio si
potrebbe rendere necessario aggiornare un contatore che memorizza il numero complessivo di articoli
presenti nel sistema. Esse potranno essere eseguite all‘interno del metodo certi che ogni modifica al
sistema sarà coerente con il processo di creazione dell‘articolo, essendo il contesto processato in
maniera transazionale.
A questo punto il container, dopo avere effettuato i debiti controlli sulla validità dei parametri
passati e dopo aver ricavato dal pool di oggetti il bean opportuno (vuoto se si sta creandone uno
nuovo come in questo caso), scinde la chiamata del metodo create() nei due corrispettivi ejbCreate() e
ejbPostCreate().
Non si entrerà nei dettagli di quelle che sono le regole relative alla firma (parametri ed eccezioni)
dei vari metodi create(), ejbCreate() ed ejbPostCreate(): per quello che concerne la creazione del
bean è sufficiente ricordare che è necessario inserire nel metodo ejbCreate() tutta la logica relativa
alla valorizzazione degli attributi semplici del bean, rimandando al metodo ejbPostCreate() la
assegnazione delle relazioni fra il bean e i suoi bean dipendenti (operazione che verrà mostrata in
seguito).
Dovrebbe essere quindi piuttosto intuitivo il significato del contenuto del metodo ejbCreate()
riportato qui di seguito
public String ejbCreate(ArticleDTO articleDTO) throws CreateException {this.setId(articleDTO.getId());this.setAbstractText(articleDTO.getAbstractText());this.setBody(articleDTO.getBody());this.setIntroducion(articleDTO.getIntroducion());this.setName(articleDTO.getName());this.setStatus(articleDTO.getStatus());this.setNotes(articleDTO.getNotes());this.setSubTitle(articleDTO.getSubTitle());this.setTitle(articleDTO.getTitle());this.setSerialName(articleDTO.getSerialName());this.setColumnName(articleDTO.getColumnName());this.setKeywords(articleDTO.getKeywords());return "";}
Si tratta di una semplice impostazione delle variabili del bean semplici; tutti gli attributi di
relazione verranno assegnati in seguito nel metodo ejbPostCreate().
La creazione del DTO: il legame con il DTOAssembler
Dopo aver analizzato il flusso di dati relativo al processo di creazione di un entity bean, flusso che
vede migrare un DTO dallo strato client a quello server, potrebbe essere interessante prendere in
considerazione il percorso inverso che i dati intraprendono: dal bean verso il client.
Questo tragitto è quello che usualmente viene effettuato in corrispondenza di una interrogazione del
client sullo strato di business logic, ad esempio per una comune ricerca per chiave primaria.
In questo caso il session effettua la ricerca dell‘entity utilizzando i parametri di ricerca ricevuti
e preleva i dati dell‘entity bean individuato per immetterli in un DTO che viene poi inviato al
client.
Per prima cosa il client invoca il metodo di ricerca sul session faà§ade: come mostrato negli articoli
precedenti, tale invocazione verrà eseguita all‘interno del business delegate associato al faà§ade, ma
per semplicità tralasceremo questo dettaglio, dando per già fatto (come mostrato in precedenza) le
operazioni di lookup della interfaccia remota di ArticleManagerSF;
ArticleDTO articleDTO = null;try {articleDTO = articleManagerSFRemote.articleFindByPrimaryKey(articleId);}catch (RemoteException re) {String msg = "Errore di invocazione remota sul metodo di faà§ade articleFindByPrimaryKey() " + re;logger.error(msg);throw new FacadeInvocationException(msg);}
Il metodo remoto del session faà§ade articleFindByPrimaryKey() è semplice nella sua implementazione:
public ArticleLocal articleFindByPrimaryKey(String id) throws EJBException {try {ArticleLocal articleLocal = articleLocalHome.findByPrimaryKey(id);return ArticleDTOAssembler.createDto(articleLocal);} catch (ObjectNotFoundException onfe) {logger.debug("nessun articolo trovato con Id=" + id);return null;}catch (Exception e) {throw new EJBException(e.getMessage());}}
Si noti l‘invocazione del metodo statico createDTO() sull‘oggetto ArticleDTOAssembler. Questo era
stato già presentato in un articolo precedente della serie ma per semplicità verrà qui riproposto:
public static ArticleDTO createDto(ArticleLocal articleLocal) {ArticleDTO articleDTO = new ArticleDTO();if (articleLocal != null) {articleDTO.setName(articleLocal.getName());articleDTO.setAbstractText(articleLocal.getAbstractText());articleDTO.setBody(articleLocal.getBody());articleDTO.setColumnName(articleLocal.getColumnName());articleDTO.setId(articleLocal.getId());articleDTO.setIntroducion(articleLocal.getIntroducion());articleDTO.setKeywords(articleLocal.getKeywords());articleDTO.setNotes(articleLocal.getNotes());articleDTO.setSerialName(articleLocal.getSerialName());articleDTO.setStatus(articleLocal.getStatus());articleDTO.setSubTitle(articleLocal.getSubTitle());articleDTO.setTitle(articleLocal.getTitle());articleDTO.setImpressions(articleLocal.getImpressions());if (articleLocal.getArticlelink() != null){articleDTO.setLinkPosition(articleLocal.getArticlelink().getPosition());}// crea i DTO dipendenti e li lega al DTO da restituire come risposta del metodoarticleDTO.setFigureDTOs(FigureDTOAssembler.createDtos(articleLocal.getFigures()));articleDTO.setIconDTO(IconDTOAssembler.createDto(articleLocal.getIcon()));articleDTO.setLightSectionDTO(SectionDTOAssembler.createLightDTO (articleLocal.getSection()));articleDTO.setWriterDTOs(WriterDTOAssembler.createDTOs(articleLocal.getWriters()));articleDTO.setAttachmentDTOs(AttachmentDTOAssembler.createDtos(articleLocal.getAttachments()));}return articleDTO;}
Si noti l‘uso come nella parte conclusiva del metodo ci si preoccupi di procedere ad assegnare gli
oggetti DTO dipendenti (DTO per le figure, icone, sezione, autori, allegati).
Il come siano gestiti gli oggetti dipendenti nel bean e conseguentemente anche nei DTO è un aspetto di
cui ci occuperemo nel prossimo articolo.
Conclusione
In questo primo articolo dedicato alla persistenza si è iniziato un cammino piuttosto lungo: gli
argomenti appena introdotti rappresentano la base per gli approfondimenti delle puntate successive.
E‘ importante comprendere lo spirito di questa serie e soprattutto l‘obiettivo principale: non un
corso di EJB ma una raccolta di indicazioni utili sia in senso assoluto che per sfruttare nel migliore
dei modi quanto appreso negli articoli precedenti.
Nel prossimo articolo parleremo di campi di relazione e dell‘importante ruolo che ricoprono sia nella
gestione delle relazioni fra i vari Entity Beans, sia nel trasferimento dei dati su i DTO.
1R. Monson-Haefel, B. Burke, S. Labourey Enterprise Java BeansO‘Reilly Media, Inc. ISBN: 059600530X2G. Puliti”Enterprise JavaBeans” Capitolo 8 di Manuale Pratico di Java. La programmazione della piattaforma J2EE Tecniche Nuove (2004)https://www.mokabyte.it/libri/manualejava2/3G. Puliti “Architetture e tecniche di progettazione EJB – III parte: la trasmissione dei dati fra gli strati tramite il pattern DTO (approfondimenti)” MokaByte 103 – Gennaio 2006https://www.mokabyte.it/2005/12/ejbarchitectures-3.htm4G. Puliti “Architetture e tecniche di progettazione EJB – III parte: la trasmissione dei dati fra gli strati tramite il pattern DTO (approfondimenti)” MokaByte 103 – Gennaio 2006https://www.mokabyte.it/2006/01/ejbarchitectures-4.htm