MokaByte 60 - Febbraio 2002 

MokaShop il negozio online di MokaByte
Progettare applicazioni J2EE multicanale
I parte: il modello MVC, la View ed il Controller

di
Giovanni Puliti
Inizia una lunga serie di articoli dedicati alla progettazione di applicazioni J2EE in cui concetti come web applications, JSP, EJB applicazioni stand alone e per embedded devices si mescoleranno fra loro

Scopo di questo progetto
Lo scopo di questo progetto è quello di raccogliere in un unico tutorial tutte le tecnologie di punta del mondo Java (la cosiddetta piattaforma J2EE) e mostrare quale sia il corretto modo di procedere nella progettazione e realizzazione poi di questo genere di applicazioni.
Si vedranno quindi non solo quali sono le tecnologie più importanti in questo settore (JSP, EJB, Servlet, etc..) ma soprattutto quali sono i pattern da utilizzare e gli schemi architetturali da adottare per realizzare applicazioni siano scalabili ed il più possibile flessibili.
Si prenderà in esame un esempio reale MokaShop, il negozio elettronico di MokaByte, tramite il quale oltre ad studiare le varie componenti di J2EE, si potrà valutare quando i modelli teorici di riferimento siano applicabili e quando invece soluzioni alternative possano adattarsi meglio al caso reale.
Il tutto dovrebbe infine confluire in un documento finale che poi è presumibile venga assimilato all'interno del progetto MokaLab.

 

MokaShop
Il negozio di MokaByte è realizzato tramite una serie di applicazioni ad interfaccia web che si basano sul famoso modello MVC, di cui si parlerà abbondantemente in seguito. Tutta la logica di gestione dei dati è gestita da un database basato sul modello documentale di Lotus Notes e quindi di tipo non relazionale; il server che gestisce le operazioni di replica distribuita dei database e di Access Control List (ACL) è Lotus Domino in versione R5. Il motivo di questa scelta non convenzionale, se per certi versi introduce evidentemente molte limitazioni rispetto a soluzioni tradizionali, offre importanti vantaggi e sarà particolarmente utile quando si dovrà spiegare come realizzare applicazioni enterprise che debbano interfacciarsi con i sistemi legacy presenti in azienda.
Inoltre tale scelta rispecchia quella che è stata l'evoluzione del progetto MokaShop, e la filosofia di base del sito.
Nella figura 1 si trova una schematizzazione semplificata della struttura di base di MokaShop: si possono individuare gli strati orizzontali e la molteplicità dei canali di comunicazione.


Figura 1 - architettura di base di MokaShop

 

Il modello multicanale
L'evoluzione degli ultimissimi tempi ha dato una spinta notevole in avanti al mondo della computazione distribuita. La nascita del commercio elettronico su internet e l'utilizzo del browser come strumento universale di comunicazione hanno di fatto modificato profondamente non solo il settore del web, ma anche ogni genere di applicazione prodotta fino ad ieri.
Il modello delle web applications ha sicuramente preso il sopravvento rispetto ad altre tipologie di applicazioni, ed anche se presenta notevoli limitazioni (connessioni stateless, mancanza di persistenza, logica solo server side, ed altro ancora) è praticamente impensabile al momento prescindere da questo modello. E' altresì vero che ormai il web non è il più solo strumento di comunicazione ed interazione con l'utente finale (anche se resta sempre il canale privilegiato), dato che nuovi canali di comunicazione si stanno facendo avanti in modo prorompente.
Per questo parlare di web applications è ormai riduttivo: il web non è altro che uno dei canali a disposizione, ma essendo sicuramente il più importante e sviluppato, per brevità spesso si utilizza tale termine per indicare applicazioni con una forte connotazione web, ma che comprendano anche oggetti come ad esempio embedded devices wireless. In tal senso finalmente tutto il mondo embedded wireless (in una parola mobile)inizia timidamente a trasformarsi da eterna promessa e concreta realtà.
L'applicazione che si andrà ad esaminare sarà quindi basata su una architettura a tre livelli (suddivisione orizzontale) in cui lo strato finale o quello di mezzo potranno essere a loro volta partizionati a seconda del tipo di client finale (suddivisione verticale): troveremo all'ormai classico strato web, troveranno posto anche applicazioni stand alone (Java for DeskOS), ed applicazioni per embedded devices (Java Micro Edition), coinvolgendo tecnologie molto in voga attualmente come XML, SOAP e Web Services.

 

Applicazioni web e web applications
MokaShop è il negozio virtuale di MokaByte, tramite il quale è possibile acquistare alcuni oggetti (libri, CDROM) ma anche effettuare pagamenti per la partecipazione a conferenze e corsi.
Per semplificare l'analisi della applicazione, si utilizzerà la dicitura "applicazione web" intendendo tutto il negozio, mentre con web application si farà riferimento a quello che in Java rappresenta tale termine, ovvero una serie di componenti particolari (Servlet, pagine JSP, componenti bean ed EJB) organizzati ed installati secondo un preciso e rigoroso schema.
Le funzionalità principali dell'applicazione in esame sono state suddivise in moduli, in modo da raggruppare nello stesso modulo più funzionalità simili o correlate fra loro: tale suddivisione è 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.
Visto che il concetto di web application si presta molto bene a questa organizzazione, si può quindi dire che il negozio di MokaByte è una applicazione web organizzata in moduli: alcuni di questi sono web applications vere e proprie, altri sono applicazioni stand alone, applicazioni per sistemi embedded-wireless, o sistemi di gestione dati di altro tipo.
Per semplificare il processo di analisi nel suo complesso, si partirà dall'analisi delle singole web applications , rimandando alle puntate successive le altre parti.
Le web application coinvolte in MokaShop sono rappresentate dai seguenti moduli principali

  • Community: tutti i servizi messi a disposizione sul sito di MokaByte sono fruibili per i soli utenti della comunità virtuale, la cui registrazione è gratuita. Dato che il negozio virtuale rappresenta solo una delle attività legate al sito, Community è una web application non legata esclusivamente al negozio virtuale. Il suo scopo è quello di creare e gestire gli account degli utenti convalidandone l'identità al momento del login. Il database utilizzato rappresenterà il punto centrale per ogni altra web application in funzione.
    E' possibile provare direttamente la web application collegandosi all'indirizzo [MB]

  • MokaCustomers: un utente della community di MokaByte non è detto che sia anche un cliente del negozio virtuale. Per questo motivo si è pensato di suddividere le due anagrafiche in modo da gestire in modo particolareggiato le due situazioni [MS].

  • MokaCart: implementa e gestisce tutta la logica di carrello virtuale, quindi raccogliendo oggetti prelevati dall'utente dalla vetrina virtuale. Anche questa web application fa parte del negozio online di MokaByte per cui può essere provata collegandosi all'indirizzo [MS].

  • MokaCatalog: gestisce il catalogo dei prodotti. In una prima versione semplificata del sistema si considera tale modulo come facente parte del MokaCart

  • MokaPay: una volta che un utente abbia effettuato tutte le operazioni di login e di raccolta dei vari elementi nel carrello, si può procedere all'acquisto utilizzando una web application apposita che provvederà ad effettuare la transazione bancaria basata su pagamento tramite carta di credito.

Per quello che è lo scopo di questa prima serie di articoli, ovvero mostrare come si debba progettare ed implementare una web application J2EE, si concentrerà l'attenzione su uno solo di questi moduli, MokaCart.
In seguito verrà mostrato come sia possibile far cooperare web application differenti fra loro, gestire il mantenimento dello stato, ed il passaggio dei parametri.

 

Web-centric vs EJB-centric
La suddivisione dell'applicazione in moduli logici è solo il primo passo da compiere durante la fase della sua progettazione. Un'altra delle decisioni da prendere in questa fase riguarda il numero di livelli di cui essa è costituita e il modo in cui le sue funzionalità, e dunque i vari componenti che le implementano, sono distribuite sui vari livelli. Nelle web applications, 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 qualche strato di business logic.
In J2EE tale strato è tipicamente costituito da componenti EJB eventualmente supportanti da semplici JavaBeans. Nel caso quindi si decida di adottare un EJB tier si parla di architettura EJB-centrica.
MokaCart è stata sviluppata utilizzando il modello MVC facendo uso in prima battuta di soli componenti JavaBeans standard (MokaCart ver. 2.0) e solo successivamente rimpiazzando i beans con componenti EJB (MokaCart 3.0). Tale scelta, è stata fatta fra l'altro al fine di mostrare come una oculata scelta della architettura di base (MVC) e la bontà della piattaforma di base (J2EE) possano rendere il sistema fortemente scalabile anche di fronte alla necessità di radicali modifiche o cambiamenti di rotta (es. da Java Beans a EJB).
Occorre quindi precisare che non sempre l'introduzione di un EJB tier sia strettamente necessaria. Per applicazioni relativamente poco complesse per le quali scalabilità non rappresenta 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: in questo caso il web tier dovrebbe preoccuparsi, fra le altre cose, della processamento 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 ed essere poco scalabile.
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.
Basare l'architettura su un EJB tier permette di utilizzarne le di funzionalità di livello enterprise quali, la gestione delle transazioni, delle connessioni e della sicurezza; in questo modo lo sviluppatore può dedicarsi alla gestione delle problematiche della business logic dell'applicazione.
Nel caso in esame, la scelta di utilizzare un database di Lotus Notes ed un server Domino in MokaCart 2.0 ha introdotto una serie di difficoltà non banali la cui risoluzione ha però automaticamente risolto anche altri problemi, come il pool di oggetti e di connessioni, tali da non rendere impellente l'introduzione dello strato di EJB.
In ogni caso il Notes ha rappresentato un'ottima palestra per mettere alla prova alcune caratteristiche di EJB, come la persistenza di tipo bean managed o l'integrazione con applicazioni legacy. Quando si affronteranno in dettaglio tali aspetti, si avrà modo di comprendere il significato di tali affermazioni.

 

Migrazioni e scalabilità
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);
  2. creare gli EJB che modellino i principali oggetti nel dominio dell'applicazione; spostare tutta la business logic negli EJB;
  3. modificare i componenti JavaBeans, progettati inizialmente per connettersi direttamente al database in modo da connettersi agli EJB;
  4. minimizzare la logica presente nelle pagine JSP spostandola il più possibile nei componenti JavaBeans o in custom tag sviluppati ad hoc.


Il modello MVC
Una delle priorità più importanti di ogni progettista è quella di realizzare una struttura che permetta di mantenere semplicità ed efficienza della applicazione, al crescere della complessità e del carico di lavoro. Accanto a tutto ciò è molto sentita la necessità di poter modificare in modo semplice alcune parti senza dover stravolgere tutto il sistema.
Il modello a tre livelli per molti versi ha rappresentato la soluzione ideale per molto tempo, anche se di recente, con il crescere della complessità delle applicazioni, ha iniziato a mostrare evidenti limitazioni.
L'evoluzione naturale a questa architettura è data dall'ormai noto modello Model View Controller (MVC) che si basa sul pattern avente lo stesso nome.
La versione 1.0 di MokaCart implementava un MVC oltremodo semplificato e per motivi di spazio non si analizzerà tale versione anche se didatticamente potrebbe essere sicuramente utile.
Il modello MVC si basa nella suddivisione funzionale degli oggetti dell'applicazione, in modo da disaccoppiarli fra loro il più possibile: in MokaCart quindi 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).
Il model rappresenta quindi i dati dell'applicazione e le regole che governano le operazioni con cui tali dati vengono acceduti e modificati. Spesso esso 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 una web application, come MokaCart, tali richieste vengono effettuate al web tier sotto forma di request get e post 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.
La figura 2 descrive le relazioni esistenti tra le parti model, view e controller di una generica architettura MVC.



Figura 2
- lo schema funzionale di MVC e le tre parti che lo compongono,
Model, View e Controller


Come nella maggior parte delle applicazioni EJB-centriche, anche in questo caso, 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 presso il controller per fare il listening degli eventi di aggiornamento del model: 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 sull'ejb-tier, il controller necessita di estendersi su entrambi i livelli; 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; è anche responsabile di mantenere lo stato della sessione dell'utente all'interno dell'applicazione.
Nel caso di MokaCart, le tre parti di MVC sono così organizzate:

  • View: Una serie di pagine JSP
  • Controller: due servlet che si occupano dello smistamento del traffico
  • Model: dei componenti JavaBeans (MokaCart v2.0) o EJB in (MokaCart v3.0) dove è inserita tutta la business logic della applicazione.

La view di MokaCart
La parte di interazione con l'utente finale è realizzata tramite una serie di pagine JSP il cui compito è esclusivamente quello di visualizzare informazioni, ed eventualmente invocare le funzioni messe a disposizioni dalla coppia Controller-Model.
La regola impone di utilizzare all'interno delle pagine JSP codice Java in forma minima (solo quanto minimamente necessario per visualizzare i contenuti dinamici), mentre sono assolutamente vietati script (Javascript o simili) inseriti nelle pagine HTML per la validazione o preprocessamento dei dati.
Inoltre sono stati utilizzati diversi custom tag al fine di ottimizzare la manutenzione delle varie pagine. L'esempio di riferimento presentato da Sun, il PetStore, al quale MokaCart si ispira fedelmente, fa un utilizzo ancora più spinto di custom tag e delle tecniche di comunicazione inter-servlet messe a disposizione dalle varie API: nella applicazione in esame invece si è cercato di limitare un utilizzo così spinto, sia per motivi di chiarezza, sia per dare spazio anche a soluzioni alternative.
Il funzionamento di MokaCart si basa su una semplice regola: tutto quello che va nella direzione server-client deve essere processato dal sistema di produzione di HTML dinamico (JSP con custom tag), mentre tutte le invocazioni dell'utente (direzione client-server) devono essere processate da due servlet appositamente installati.


Figura 3
- flusso delle invocazioni per la coppia View-Controller


Ogni pagina JSP verrà composta in modo modulare utilizzando la tecnica detta dei template processing.
Un template non è altro che una pagina JSP in cui si definiscono tutti gli aspetti grafici di contorno, facendo attenzione a riservare le varie parti che dovranno essere inserite dinamicamente.
Ad esempio il template di base utilizzato in MokaCart prevede 5 blocchi dinamici:
il titolo della pagina

  • la menubar
  • un header
  • un body
  • un footer



Figura 4
- template jsp utilizzato per la web application MokaCart. Si notano le aree (evidenziate qui in verde) che compongono la struttura del template


Come si può notare dalla figura 4 gli elementi grafici utilizzati sono molto semplici, ma un template più complesso potrebbe essere sostituito in modo praticamente indolore, andando così a cambiare radicalmente i connotati della applicazione senza dover riscrivere nemmeno una riga di codice.
Per poter inserire una sezione all'interno del template si utilizza il tag define contenuto nella MokaByteTagLibrary tramite il prefisso mbtl.
Ad esempio, tralasciando per un momento la parte di grafica e l'HTML statico si potrebbe pensare di strutturare il file template.jsp nel seguente modo

. . .
<!-- pagetitle -->
<mbtl:define section="pagetitle" printable='true'>
</mbtl:define>

. . .
<!-- menubar -->
<mbtl:define section="B1" printable='true'></mbtl:define>
<mbtl:define section="B2" printable='true'></mbtl:define>
<mbtl:define section="B3" printable='true'></mbtl:define>
<mbtl:define section="B4" printable='true'></mbtl:define>
. . .

<!-- header -->
<mbtl:define section="header" printable='true'> </mbtl:define>

<!-- body -->
<mbtl:define section="body" printable='true'> </mbtl:define>


<!-- footer -->
<mbtl:define section="footer" printable='true'> </mbtl:define>


Ce si può notare il tag define definisce una sezione; il parametro printable impostato a true indica che il contenuto di tale sezione verrà sostituito dal corpo del tag stesso. In alternativa si può utilizzare un file .jsp per riempire la sezione (si veda il corrispettivo tag insert).
In questo caso tutti i body del tag vuoti, non verrà visualizzato niente, ma più genericamente schema potrebbe essere invece utilizzato per definire un comportamento di default del template.
Volendo fare in modo che la pagina LoginFailed.jsp utilizzi la struttura e la grafica del template, sarà sufficiente scrivere

<%@ taglib uri='mbtl' prefix='mbtl' %>

<mbtl:template name='pageTemplate' content='template.jsp'>
<mbtl:insert section='title' printable='true'>
Errore di login
</mbtl:insert>
<mbtl:insert section='body' printable='true'>

… … … codice HTML della pagina JSP … … …

</mbtl:insert>
</mbtl:template>


A questo punto la coppia template+pagina JSP definisce quello che in genere si definisce una vista. Anche in questo caso si può notare come il tag insert accetti un parametro printable.
Se è impostato a true, il tag andrà a sostituire nel template il codice HTML presente all'interno del tag (nell'esempio la scritta "Errore di login"), ma si potrebbe anche utilizzare un altro file jsp per comporre il template. Ad esempio

<mbtl:template name='pageTemplate' content='template.jsp'>
<mbtl:insert section='body' file='LoginFailedBody.jsp'>
</mbtl:insert>

</mbtl:template>


La tecnica appena vista rappresenta una contrazione dello schema utilizzato in Pet Store, che utilizza file di configurazione in XML.
In realtà ci possono essere molte possibili soluzioni o varianti della tecnica di template processing, ognuna delle quali mostra vantaggi e svantaggi. Si veda ad esempio quanto riportato in [JW].
Resta da vedere come funzioni il bean template che implementa i due tag utilizzati questo oggetto. Dato che lo spazio a disposizione in questo articolo non sarebbe sufficiente, si rimanda ad uno dei prossimi articoli per la analisi nel dettaglio dei vari componenti utilizzati. Per chi fosse interessato ad utilizzare da subito un esempio basato sulla tecnica appena vista, potrà utilizzare trovare interessanti spunti sia all'interno di PetStore [PT] ma anche in [AJSP].

 

Le azioni ed il controller
Ogni volta che l'utente si trova di fronte ad una pagina HTML prodotta dinamicamente con la tecnica appena vista, potrà effettuare due tipi di operazioni: eseguire una azione o richiedere visualizzare un'altra pagina HTML: la pagina che a questo punto nella terminologia MVC prende il nome di vista.
Le azioni e le viste sono quindi i due elementi che dovranno essere gestiti dal Controller: per questo motivo il controller sarà composto da due semplici servlet i quali, provvedono ad eseguire una azione o visualizzare una vista.
Al fine di indirizzare le invocazioni in modo automatico ad uno o all'altro servlet, si utilizza un banale mapping fra url-pattern e servlet della web application. Ad esempio in MokaCart tutti i link corrispondenti ad azioni sono composti secondo lo schema

codice-azione.run

mentre quelli che portano alla visualizzazione di una altra pagina, avranno la seguente forma

codice-vista.show

L'associazione fra tali URL simbolici ed i due servlet configurando opportunamente la web application tramite il file web.xml. Tale URL-pattern è poi associato ad un servlet tramite la seguente porzione di codice XML

<servlet-mapping>
<servlet-name>
action
</servlet-name>
<url-pattern>
*.run
</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>
router
</servlet-name>
<url-pattern>
*.show
</url-pattern>
</servlet-mapping>

dove action e template sono i due nomi logici dei servlet. Infatti sempre nel file web.xml si troverà la seguente porzione di codice

<servlet>
<servlet-name>
action
</servlet-name>
<servlet-class>
com.mokabyte.mokashop.servlet.ActionServlet
</servlet-class>
<init-param>
<param-name>
action-mappings
</param-name>
<param-value>
/../config/mokacartnew/actions.properties
</param-value>
</init-param>
</servlet>

<servlet>
<servlet-name>
router
</servlet-name>
<servlet-class>
com.mokabyte.mokashop.servlet.RouterServlet
</servlet-class>
<init-param>
<param-name>
router-mappings
</param-name>
<param-value>
/../config/mokacartnew/mappings.properties
</param-value>
</init-param>
</servlet>

Si noti la struttura del parametro utilizzato in entrambi i casi definire il path del file properties.
Tale scelta è concettualmente molto semplice, ma elimina in modo drastico il problema della configurazione delle web application.
Si consideri ad esempio il caso del parametro indicante il nome del database al quale riferirsi: sicuramente tale stringa cambierà nel passare dall'ambiente di sviluppo a quello di test a quello di produzione.
Dato che l'operazione di spostamento della web application corrisponde a dover muovere una directory da un server ad un altro, la soluzione a questo problema consiste nello spostare fuori da tale directory tutti i file di configurazione. Se questo in fase di sviluppo corrisponde ad avere una cartella all'interno del progetto (vedi figura 5);


Figura 5
- organizzazione delle directory in fase di sviluppo
utilizzando il tool JBuilder 6.0

in produzione si farà uso di una pseudo web application che svolgerà la funzione di configuratore di tutte le web application installate (figura 6).


Figura 6 - Organizzazione delle directory in ambiente di produzione
utilizzando il server Tomcat

Per maggiori approfondimenti su tale tecnica si rimanda al solito ad una delle prossime puntate.


Esecuzione delle azioni
Riconsiderando il meccanismo di gestione delle azioni, dovrebbe essere chiaro come tutte le invocazioni del tipo .run verranno intercettate dal primo servlet: questo, dopo aver ricavato il codice della azione (che precede il run), manda in esecuzione la classe corrispondente alla azione.
Infatti per ogni azione da eseguire in MokaCart sono state definite opportune classi Java il cui metodo perform() contiene le istruzioni da eseguire.
L'associazione fra codice della azione e classe Java è memorizzata in un file .properties o XML. Supponendo ad esempio di dover mappare l'operazione di login, e di utilizzare il formato properties, si potrà trovare la seguente riga

login=com.mokabyte.mokashop.mokacart.actions.LoginAction

Il caricamento in memoria della classe corrispondente alla action desiderata è il seguente

private ActionFactory factory = new ActionFactory();
String actionClass = getActionClass(req);
Action action = factory.getAction(actionClass,
getClass().getClassLoader());

La classe LoginAction sarà quella che eseguirà materialmente l'operazione di login (non direttamente ma appoggiandosi alla business logic del bean), e restituirà un codice di risposta che potrebbe essere

login-failed

nel caso di fallimento del login, oppure

login-successed

nel caso di login effettuato con successo. Il codice potrebbe essere

String response = action.perform(this,req,res);


Il servlet ActionServlet a questo punto effettua un redirect automatico alla risorsa (che potrebbe essere una altra azione oppure una nuova vista logica), corrispondente al codice restituito dalla perform(). Per sapere quale URL debba essere restituito, il servlet utilizzerà un altro file che si potrebbe chiamare mappings.properties (sempre nell'ipotesi di utilizzare tale formato). Tale file contiene righe del tipo

user-logged=catalog.show
user-not-logged=login.show
login-successed=is-user-customer.run

Come si può notare al codice user-logged è associata una vista con l'URL simbolico catalog.show, mentre al user-not-logged la vista login.show. Entrambi i file verranno caricati alla inizializzazione del servlet. Ad esempio

Properties properties = null;
try {
properties = new Properties();
ServletContext sc = getServletContext();
InputStream is;
is =sc.getResourceAsStream(config.getInitParameter(
"mappings.properties"));
properties.load(is);
}
catch(Exception e) { … }

Anche se tale meccanismo può apparire macchinoso, in realtà esso risulta essere molto semplice ed automatico. In sintesi

  • il servlet ActionServlet viene eseguito per tutte le invocazioni del tipo *.run
  • il servlet ActionServlet ricava il codice della azione
  • il servlet ActionServlet carica in memoria la classe corrispondente al codice
  • il servlet ActionServlet invoca sulla azione il metodo perform() che restituisce un codice. Tale codice viene utilizzato dal servlet ActionServlet per ottenere l'URL simbolico (*.run o *.show) a cui inoltrare la richiesta il controllo passa alla risorsa invocata: nel caso in cui si tratti di un *.run si avrà nuovamente l'esecuzione dello stesso ciclo, altrimenti il controllo passerà al RouterServlet


Per quanto riguarda invece gli URL simbolici del tipo *.show, essi in maniera del tutto analoga verranno serviti da un altro servlet, il RouterServlet, che utilizzando lo stesso mappings.properties, nella forma più semplice esegue banalmente un forward verso la pagina corrispondente (in genere JSP o HTML).

Questo meccanismo ha il notevole vantaggio di poter in ogni momento permettere la modifica del flusso delle pagine da visualizzare e delle azioni da eseguire senza dover toccare in alcun modo ne le pagine JSP, ne il codice Java dei due servlet (che svolgono la funzione di smistamento del traffico).
Inoltre se in un qualsiasi momento si volesse aggiungere una nuova tipologia di URL simbolici, sarebbe sufficiente aggiungere una linea al file web.xml e implementare il servlet corrispondente. Ad esempio .jump e JumpServlet potrebbero essere utilizzati per realizzare il salto fra web application differenti, problema questo in genere molto sentito a causa della separazione del contesto e di cui si parlerà in seguito.

Si tenga ben presente che una azione deve essere considerata come una procedura batch che effettui invocazioni sui metodi dei vari bean coinvolti. Per questo una action non contiene business logic (ad esempio non si connetterà mai direttamente ad un database per modificare il contenuto di una tabella), ma piuttosto porterà alla sua esecuzione.
Conclusione
Sebbene molti aspetti sono stati lasciati volutamente in sospeso (specie agli aspetti di programmazione dei vari custom tag utilizzati) arrivati a questo punto si dovrebbe concentrare l'attenzione sul concetto di base che regola la struttura di tutta l'applicazione: il modello MVC, la separazione fra contesti logici, l'utilizzo dei template e delle azioni, sono tutti concetti che ogni progettista e programmatore di web application dovrebbe tenere sempre bene a mente.
Nel articolo del mese prossimo parleremo del model della web application, e come questo possa essere progettato ed implementato poi in modo scalare, passando prima da semplici JavaBeans e poi a EJB.

 

Bibliografia
[PT] - "Pet Store" url della web application di esempio; java.sun.com
[AJSP] - "Advanced JSP"
[JW] - "UI design witrh Tiles and Struts" di Prakash Malani pubblicato su JavaWorld Gennaio 2002 - www.javaworld.com/javaworld/jw-01-2002/jw-0104-tilestrut.html
[MC] - La web application Community si trova all'indirizzo di http://www.mokabyte.it/community
[MS] - Le web application del negozio online di MokaByte si possono provare collegandosi all'indirizzo di http://www.mokabyte.it/mokashop

 

Ringraziamenti
Per la stesura di questo articolo, e dei successivi, vorrei ringraziare in modo particolare Mario Fusco, il cui contributo è stato essenziale durante la fase di raccolta delle informazioni e di stesura di alcune parti.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it