Realizzare un Pet Store/parte prima

Posted On 13 Gen 2008
Comment: Off

tutorialjava Tutorial Java – L’applicazione Pet Store, sviluppata da Sun per dare un esempio concreto di utilizzo delle tecnologie J2EE, modella una tipica applicazione di e-commerce: un negozio on-line di animali domestici. L’applicazione, insieme ad i suoi sorgenti, è liberamente scaricabile dal sito della Sun all’indirizzo: java.sun.com/blueprints/code/index.html. Come un qualsiasi tipico sito di e-commerce, il Pet Store mostra al cliente il catalogo dei prodotti disponibili suddivisi per categorie. Il cliente mette i prodotti di proprio interesse nel carrello e quando è pronto ad effettuare l’ordine, l’applicazione mostra la fattura di vendita comprendente l’elenco di tutti i prodotti nel carrello con le relative quantità e prezzo per ciascun prodotto, insieme con il costo totale. Il cliente, a questo punto, può procedere con l’ordine, inserendo i dati necessari a completarlo quali il numero di carta di credito e l’indirizzo di consegna, oppure modificarlo o cancellarlo del tutto.

fig1


  
I moduli dell’applicazione
Lo scenario appena delineato determina quali debbano essere le principali funzionalità di una qualsiasi applicativo di e-commerce. Nel caso del Pet Store tali funzionalità sono state suddivise in moduli, in modo da raggruppare nello stesso modulo più funzionalità simili o correlate fra loro. La suddivisione di un’applicazione complessa in moduli quanto più è possibile disaccoppiati, unitamente ad una esaustiva definizione delle interfacce mediante le quali i moduli interagiscono fra loro, è sempre auspicabile in quanto consente di demandare la progettazione e lo sviluppo dei diversi moduli a team diversi che possono lavorare indipendentemente fra loro o addirittura di subappaltare la realizzazione di alcuni di essi a terzi, magari perché particolarmente competenti di un determinato settore. In particolare l’applicazione Pet Store appare composta dai seguenti moduli principali:


• Signon: crea e gestisce gli account degli utenti convalidandone l’identità al momento del login
• Customer: gestisce i profili dei clienti
• Cart: gestisce i carrelli
• Catalog: gestisce il catalogo dei prodotti
• Mailer: si occupa di inviare ai clienti le mail di notifica degli ordini effettuati
• Uidgen: utilizzato per generare id unici

La decomposizione in moduli dell’applicazione è evidenziata nel suo packaging.


fig21

 
Web-centric vs. EJB-centric
La suddivisione dell’applicazione in moduli logici è solo il primo passo compiuto durante la fase della sua progettazione. Un’altra delle decisioni prese in questa fase riguarda il numero di livelli di cui essa è costituita e il modo in cui le sue funzionalità, e dunque le classi che le implementano, sono distribuite sui vari livelli. Nelle applicazione web, come il Pet Store, alcuni livelli sono sempre presenti: il client tier rappresentato dal browser, il web tier costituito dal web server ed il database tier che conserva i dati persistenti dell’applicazione. La principale scelta da fare in questo caso consiste dunque nel decidere se il web tier debba accedere direttamente ai dati presenti sul database (architettura web-centrica) oppure passare attraverso un EJB tier (architettura EJB-centrica), scelta quest’ultima che è stata fatta nel caso del Pet Store. Occorre tuttavia precisare che non sempre l’introduzione di un EJB tier è sempre strettamente necessaria. Per applicazioni relativamente poco complesse e per cui la scalabilità non sia un requisito essenziale, il non fare uso di un EJB tier potrebbe rivelarsi una scelta vincente soprattutto in termini di velocità nella progettazione e nello sviluppo dell’applicazione stessa. Ovviamente, però, in assenza di un EJB tier, il web tier diviene responsabile della gestione di quasi tutte le funzionalità dell’applicazione. Ad esempio, in questo caso, il web tier dovrebbe preoccuparsi, fra le altre cose, della processazione degli ordini, della gestione delle transazioni e di mantenere un pool di connessioni con il database server. Come risultato della concentrazione di tutte queste funzionalità nel web tier ne deriva che, al crescere della complessità dell’applicazione, il software ha la tendenza a diventare monolitico e ad avere una bassa scalabilità. Ciò detto, diviene comprensibile come nel caso di applicazioni piccole in partenza, per le quali sia stata quindi scelta un’architettura web-centrica, e che siano poi cresciute con il tempo, possa rendersi necessaria l’aggiunta in un secondo tempo di uno strato EJB. Ne deriva che è buona norma progettare un’applicazione web-centrica in modo che sia facile, in caso di necessità, migrarla in una EJB-centrica. Il principale vantaggio di un’architettura che preveda un EJB tier, consiste nel fatto che quest’ultimo ha integrato una serie di funzionalità di livello enterprise quali, la gestione delle transazioni, delle connessioni e della sicurezza che liberano lo sviluppatore dall’onere di doverle implementare da sé consentendogli di potersi totalmente dedicare alle problematiche legate alla business logic dell’applicazione. Usualmente è possibile migrare un’architettura web-centrica in una ejb-centrica applicando i seguenti passi:


1. modificare il design dell’applicazione in modo da utilizzare un’architettura MVC (model-view-controller); anche il Pet Store utilizza questa architettura i cui dettagli verranno discussi nel seguito;
2. creare gli EJB che modellino i principali oggetti nel dominio dell’applicazione;
3. spostare tutta la business logic negli EJB;
4. modificare i componenti JavaBeans, progettati inizialmente per connettersi direttamente al database in modo da connettersi agli EJB;
5. minimizzare la logica presente nelle pagine JSP spostandola il più possibile nei componenti JavaBeans o in custom tag sviluppati ad hoc.

fig3


 
 
L’architettura MVC
Ad un’analisi più accurata, l’applicazione Pet Store appare logicamente suddivisa in tre categorie di oggetti: ci sono oggetti che hanno a che fare con gli aspetti legati alla sua presentazione (view), altri che riguardano le regole legate alla business logic ed ai dati (model), altri ancora che accettano ed interpretano le richieste degli utenti e sono responsabili di controllare che tali richieste siano più o meno legittime per poi esaudirle se è possibile (controller). Questa tecnica di suddividere funzionalmente gli oggetti dell’applicazione, in modo da disaccoppiarli fra loro il più possibile, è appunto detta architettura model-view-controller. Nell’architettura MVC, il model rappresenta i dati dell’applicazione e le regole che governano le modalità con cui questi dati vengono acceduti e modificati. Spesso il model rappresenta un’approssimazione software degli oggetti realmente presenti nel dominio dell’applicazione. Il model notifica la view dei suoi cambiamenti e mette a disposizione della view un modo per interrogare il model circa il proprio stato.
La view ha invece il compito di effettuare il rendering del model. In altri termini, questa accede ai dati contenuti nel model e decide le modalità con cui questi dati debbano essere presentati. Quando il model cambia è responsabilità della view di mantenere la sua presentazione coerente con tali cambiamenti. La view, infine, ha il compito di inviare le richieste dell’utente al controller. Quest’ultimo definisce il comportamento dell’applicazione: esso interpreta le richieste dell’utente e le mappa in azioni che verranno eseguite sul model. In un’applicazione web, come il Pet Store, tali richieste arrivano al web tier sotto forma di GET e POST request HTTP. Inoltre, in base alle richieste dell’utente ed agli effetti che tali richieste implicano sul model, il controller seleziona la view che verrà utilizzata per effettuare il rendering del model così modificato.
In particolare nel Pet Store, come nella maggior parte delle applicazioni EJB-centriche, gli EJB di tipo entity, che riflettono i dati immagazzinati nella base di dati, rappresentano il model dell’architettura. Per quanto riguarda la view, i dati memorizzati nel model vengono replicati lato web da componenti JavaBeans. Questi componenti si registrano al controller per ascoltare gli eventi di aggiornamento del model e quando ricevono uno di tali eventi interrogano gli entity bean per allineare il proprio stato. Il rendering dei dati presenti nei componenti JavaBeans viene poi ovviamente effettuato mediante pagine JSP. Infine, per ciò che concerne il controller, esso è probabilmente la parte più complessa dell’applicazione. Mentre infatti la view è concentrata sul web-tier e il model risiede unicamente sul ejb-tier, il controller necessita di estendersi su entrambi i livelli e proprio a causa di tale complessità può essere funzionalmente suddiviso nelle seguenti sottoparti:


• request processor: converte le request HTTP in eventi comprensibili dal resto dell’applicazione, consentendo di concentrare in un unico punto le processazioni specifiche del protocollo HTTP e quindi rendendo il resto dell’applicazione indipendente dal tipo di client utilizzato;
• web controller: inoltra gli eventi generati dal request processor, all’ejb controller, assicurando che il risultato dell’aggiornamento del model, effettuato da quest’ultimo, venga propagato ai componenti JavaBeans ovvero alla view;
• ejb controller: accetta gli eventi inviati dal web controller e modifica il model coerentemente con tali eventi; e anche responsabile di mantenere lo stato della sessione dell’utente all’interno dell’applicazione.


Nel seguito dell’analisi dell’applicazione verranno presi in considerazione separatamente il model, la view ed il controller del Pet Store, evidenziandone le scelte architetturali ed i dettagli implementativi.


Il model
Il model di un’applicazione può essere visto come la collezione dei dati che ne costituiscono lo stato, insieme con le regole con cui tale stato cambia in risposta alle richieste effettuate dagli utenti. Le modalità con cui, nel Pet Store, la view legge i dati immagazzinati nel model sono molteplici e dipendenti dalla persistenza e dallo scope di visibilità dei dati stessi. Ad esempio, mentre i dati relativi al catalogo dei prodotti sono normalmente acceduti in sola lettura e sono identici per tutti gli utenti, quelli relativi alla lista dei prodotti inclusi in un determinato carrello sono suscettibili di modifiche anche frequenti e sono relativi ad una singola sessione. Tali differenze si riflettono nelle diverse scelte architetturali fatte nella progettazione dei model dei vari moduli che compongono l’applicazione, e pertanto nel seguito verranno analizzati singolarmente.


fig4

 
Il catalogo dei prodotti
Il catalogo dei prodotti è l’unico modulo del Pet Store in cui l’accesso ai dati viene effettuato senza passare per un ejb tier. In questo modulo infatti il DAO (Data Access Object), ovvero la parte del model che si occupa di accedere fisicamente ai dati, implementato dalla classe
com.sun.j2ee.blueprints.catalog.dao.CatalogDAOImpl, esegue delle query dirette sul database via JDBC, formattando il risultato di tali query in strutture dati che poi la view è in grado di manipolare. Ad esempio, tramite il metodo getProducts() riportato nel seguito, è possibile ottenere un oggetto Page, che racchiude una lista di un certo numero di prodotti (tanti quanti definiti dal parametro count a partire dallo start-esimo) appartenenti ad una determinata categoria, oltre al numero totale di prodotti presenti nella categoria stessa:

public Page getProducts(String categoryID, int start, int count, Locale l)
throws CatalogDAOSysException {

Connection c = null;
PreparedStatement ps = null;
ResultSet rs = null;
Page ret = null;

try {
c = getDataSource().getConnection();

// Count.
ps = c.prepareStatement(“select COUNT(*) “
+ “from (product a join product_details b on “
+ “a.productid=b.productid) where locale = ? “
+ “and a.catid = ? “,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ps.setString(1, l.toString());
ps.setString(2, categoryID);
rs = ps.executeQuery();
rs.first();
int total = rs.getInt(1);
rs.close();
ps.close();

// Select.
ps = c.prepareStatement(“select a.productid, name, descn “
+ “from (product a join product_details b on “
+ “a.productid=b.productid) where locale = ? “
+ “and a.catid = ? order by name”,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ps.setString(1, l.toString());
ps.setString(2, categoryID);
rs = ps.executeQuery();
if (start >= 0 && start < total) {
List items = new ArrayList();
rs.absolute(start+1);
do {
items.add(new Product(rs.getString(1).trim(),
rs.getString(2).trim(), rs.getString(3).trim()));
} while (rs.next() && (–count > 0));
ret = new Page(items, start, total);
} else {
ret = Page.EMPTY_PAGE;
}
rs.close();
ps.close();
c.close();
return ret;
} catch (SQLException se) {
throw new CatalogDAOSysException(“SQLException: “+se.getMessage());
}
}

In realtà è anche possibile accedere al catalogo mediante lo stateless session bean com.sun.j2ee.blueprints.catalog.ejb.CatalogEJB, ma nell’implementazione attuale del Pet Store questo ejb non fa altro che richiamare a sua volta il DAO. Ad esempio, il metodo getProducts() precedentemente riportato è implementato nell’ejb come segue:

public Page getProducts(String categoryID, int start, int count, Locale l) {
try {
return dao.getProducts(categoryID, start, count, l);
} catch (CatalogDAOSysException se) {
throw new EJBException(se.getMessage());
}
}


E’ poi compito della view scegliere quale delle due implementazioni di accesso ai dati disponibili utilizzare. All’interno della classe com.sun.j2ee.blueprints.catalog.client.CatalogClientHelper, che viene utilizzata come JavaBean nelle pagine JSP per accedere al catalogo, il metodo getProducts()è infatti così implementato:


public Page getProducts(String categoryID, int start, int count, String localeString) throws CatalogClientException {
Locale locale = getLocaleFromString(localeString);
return useFastLane
? getProductsFromDAO(categoryID, start, count, locale)
: getProductsFromEJB(categoryID, start, count,locale);
}


Basta quindi modificare il valore della variabile booleana useFastLane per switchare tra le due implementazioni disponibili. E’ quindi evidente che, se si dovesse decidere in un secondo tempo di implementare l’accesso al catalogo mediante ejb, l’integrazione di tale componente con il resto dell’applicazione sarà immediata. Tale flessibilità nell’accesso ai dati è proprio dovuta all’utilizzo dei DAO, che rappresentano uno dei più utilizzati pattern dell’architettura J2EE, consentendo di incapsulare la logica con cui tali dati vengono letti e presentando una ben determinata interfaccia alle componenti che li invocano. Nell’esempio appena visto il risultato dell’invocazione del model attraverso il metodo getProducts() ha sempre come risultato un oggetto di tipo Page (che nella nomenclatura del pattern DAO assume il ruolo di DTO, ovvero Data Transfer Object) contenente la lista dei prodotti richiesti. Sarà poi compito della view effettuare il rendering di questa lista per presentarla all’utente. Pertanto, a patto che le caratteristiche dell’oggetto Page restino invariate, il modo con cui questo oggetto viene costruito, e quindi il modo con cui i dati vengono reperiti dal database, può cambiare in maniera del tutto trasparente per la view.


Il carrello
Nel Pet Store il carrello è stato implementato mediante la classe com.sun.j2ee.blueprints.cart.ejb.ShoppingCartLocalEJB che è uno stateful session bean. Come noto il ciclo di vita di questo tipo di ejb è strettamente legato ad una singola sessione HTTP. Inoltre esso è in grado di mantenere informazioni specifiche per un singolo client preservando il proprio stato attraverso le varie invocazioni del client stesso. Ciò ne fa la struttura dati ideale per mantenere le informazioni relative ad un carrello, consentendo di aggiungere e rimuovere prodotti dal carrello oppure di recuperare i prodotti che vi sono già stati inseriti evitando di dover fare accessi al database sia in scrittura che in lettura.


fig5


 Il cliente
Nell’applicazione in esame le informazioni relative ai clienti registrati vengono accedute mediante un entity bean di tipo cmp implementato dalla classe com.sun.j2ee.blueprints.customer.ejb.CustomerEJB. In particolare attraverso questo bean è possibile accedere ad altri entity bean che fanno riferimento ad altri tipi di informazioni riguardanti il cliente stesso quali il proprio account ed il proprio profilo. La scelta di utilizzare entity bean invece di accedere ai dati direttamente via JDBC, com’è stato fatto per il catalogo, dipende dal fatto che in questo caso i dati non sono acceduti in sola lettura, ma sono modificabili dall’utente. Così facendo è infatti possibile ottenere, senza alcun ulteriore sforzo per lo sviluppatore, funzionalità indispensabili messe a disposizione dall’EJB container quali la transazionalità e la sicurezza. A conferma di ciò, altri moduli con questa stessa caratteristica, come quello che gestisce gli ordini, sono stati implementati allo stesso modo.



About the Author

Related Posts