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:
- modificare
il design dell'applicazione in modo da utilizzare
un'architettura MVC (model-view-controller);
- creare
gli EJB che modellino i principali oggetti nel dominio
dell'applicazione; spostare tutta la business logic
negli EJB;
- modificare
i componenti JavaBeans, progettati inizialmente per
connettersi direttamente al database in modo da connettersi
agli EJB;
- 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.
|