Realizzare una applicazione Java EE significa anche pensare allo strato di presentazione. Si tratta in definitiva del layer che consente l‘interazione con l‘utente e permette a quest‘ultimo di “operare” sulla nostra applicazione. Attualmente, realizzare lo strato di presentazione significa sostanzialmente realizzare una web application.
Introduzione
Dopo aver diffusamente parlato delle tecnologie e delle modalità di sviluppo dei layer di business e di quello di persistenza, è finalmente giunto il momento di parlare dello strato di presentation: come è ormai ben noto, realizzare lo strato di interazione con l’utente significa in questo momento implementare una applicazione web. L’utilizzo del browser come piattaforma client-side dove far eseguire l’applicazione è ormai una pratica consolidata tanto che nessuno più si domanda del perche’ e percome delle più moderne applicazioni web 2.0 che arrivano nelle nostre case o sui nostri telefonini.
Sviluppare un’applicazione web ha da sempre richiesto tempi maggiori rispetto a quanto necessario per la realizzazione di una comune applicazione client; per questo motivo l’obiettivo delle varie tecnologie (Java e non solo) che si sono succedute negli anni è stato quello di semplificare e velocizzare i tempi di produzione, cercando al contempo di migliorare il livello di interattività delle GUI web, vero tallone di Achille di questo genere di applicativi legati al rigido schema resquest-response di HTTP.
Volendo limitare la sfera di analisi al mondo Java, possiamo dire che in questo momento lo scenario tecnologico sta proponendo due soluzioni apparentemente differenti, ma che probabilmente a breve convergeranno verso una fusione (segnali evidenti stanno già confermando questa sensazione): da un lato abbiamo le tecnologie web server-side che, pur introducendo innovazioni sostanziali (vedi la programmazione a eventi RAD-oriented), sono orientate verso lo sviluppo di applicazioni che saranno poi eseguite all’interno di un web container, quindi sul lato server. In questo ambito probabilmente la tecnologia Java che sta riscuotendo il maggior successo è certamente JSF che sta vivendo una importante evoluzione verso il mondo JavaScript/AJAX grazie ad alcuni progetti gestiti da attori diversi e focalizzati sullo sviluppo di nuovi componenti visuali. Parleremo di questa evoluzione in uno dei prossimi articoli.
Dall’altra parte troviamo tutte quelle tecnologie di matrice JavaScript e che quindi basano la loro forza sull’offrire all’utente un’elevata interattività e semplicità d’utilizzo: si pensi agli ormai stranoti Google Maps o all’ancor più sorprendente Google Spreadsheet.
La cosa interessante del mondo JavaScript è che recenti tecnologie stanno offrendo ai programmatori la possibilità di sviluppare direttamente in Java, dando poi la possibilità di convertire il codice così realizzato opportunamente in codice lato client. Probabilmente questa sarà la soluzione che metterà d’accordo tutti.
Accanto a questi due scenari, troviamo soluzioni più o meno proprietarie (anche se il termine in alcuni casi è improprio) che portano avanti soluzioni differenti ma con lo stesso obiettivo di base: offrire uno strumento che permetta di realizzare applicazioni web in modo semplice ma al contempo offrendo un elevato livello di interazione: i due esempi senz’altro più interessanti sono Flex [FLEX] di Adobe e JavaFX. Il primo permette di realizzare applicazioni Java che sono poi compilate in modo da essere eseguite sul runtime Flash pluggato all’interno del browser. I risultati sono molto interessanti tanto che se ne iniziano a vedere esempi di adozione anche in scenari enterprise. Il secondo, JavaFX, lo menzioniamo in questo spazio solo per dovere di cronaca: la tecnologia non ha ancora raggiunto i livelli di diffusione delle altre concorrenti e non offre al momento lo stesso livello di maturità che possiamo trovare ad esempio in JSF. JavaFX, di cui comunque parleremo su MokaByte molto presto, è una specifica proposta da Sun che la sta portando avanti ormai da un paio di anni, per cui tutto lascia pensare che non finirà presto nel dimenticatoio.
Questo mese concentriamo l’attenzione sulla specifica JSF, mostrando le due principali modalità di utilizzo di questo potente framework. Nelle puntate successive cercheremo, per quanto lo spazio di un articolo ci permette di fare, di analizzare le altre soluzioni che qui abbiamo solamente accennato.
Filosofia RAD
JSF è stata la prima tecnologia che, unitamente a un ambiente di sviluppo RAD, ha permesso di realizzare applicazioni web in modo differente. Per prima cosa verrebbe da chiedersi cosa sia RAD (Rapid Application Development):
- RAD è disegnare e associare il codice: se si dispone di un ambiente di sviluppo evoluto (requisito indispensabile) si può disegnare l’interfaccia utente e associare il codice da eseguire in corrispondenza degli eventi generati da o destinati a ogni componente dell’interfaccia grafica. I programmatori Visual Basic o Delphi conoscono bene e ormai da tempo questa modalità.
- RAD è utilizzare i componenti: grazie all’utilizzo di librerie di componenti grafici, lo sviluppo diviene molto più semplice dato che si riduce il numero di linee di codice da scrivere. Le applicazioni si disegnano con il mouse definendo i vari componenti dove si inseriscono le linee di codice da eseguire in funzione degli eventi prodotti.
- RAD è programmare a eventi: oltre a disegnare la GUI, il programmatore può utilizzare il motore ad eventi offerto direttamente dal framework. Ad esempio possiamo associare del codice che verrà eseguito per ogni mousedown su un bottone o tutte le volte che il valore di un text field viene cambiato.
Negli anni RAD è stato spesso associato a linguaggi e strumenti di lavoro non molto vicini alla filosofia Java EE (il Visual Basic è probabilmente il caso più evidente), finendo per assumere una connotazione negativa. RAD di per se non è ne bene ne male, ma è un modo di lavorare particolare. Qualcuno probabilmente obietterà che sviluppare a un livello troppo alto, in ambito enterprise, può essere una limitazione: la tecnologia JSF da questo punto di vista può essere considerata come una moderna e potente automobile che si possa guidare tramite un fascinoso e semplice cruscotto digitale. JSF però in ogni momento permette di aprire il cruscotto (o il cofano) e di andare a controllare e personalizzare il comportamento di ogni singolo pezzo. JSF da questo punto di vista si distingue rispetto ad altre tecnologie RAD, consentendo varie modalità di utilizzo: da un RAD puro (dove si programma pesantemente con mouse e finestra delle proprietà e dove si finisce per avere una applicazione a due livelli) è possibile passare a un RAD ibrido in cui si connette lo strato di visualizzazione scritto in JSF con la business logic realizzata secondo il classico schema a tre livelli (anche se in realtà i moderni IDE/RAD consentono di sviluppare in modo visuale anche se si vuole popolare un componente tabella con il risultato dell’invocazione di un session bean). Vediamo cosa si intende quindi con questi due approcci.
RAD puro
In questo caso si sviluppa l’applicazione secondo un classico approccio a a 2 livelli: il codice è annegato dietro le quinte dei vari componenti grafici che compongono ogni pagina.
Se si prende come riferimento l’esempio riportato in uno dei tutorial online di Netbeans (vedi [RAD]) si può effettivamente notare come la realizzazione di questo genere di applicazioni sia veramente rapido e semplice. Con pochi colpi di mouse e qualche semplice configurazione si ottiene una pagina JSF che si connette al database e visualizza il risultato di una query in un componente grafico. L’architettura finale dell’applicazione è molto probabilmente in netta antitesi con quanto visto fino a questo momento in questa serie di articoli: in pratica non fa uso in alcun modo di layer sottostanti, ne’ di business logic, ne’ tantomeno di mapping sul motore di persistenza. Per realizzare una applicazione RAD secondo l’approccio visuale puro si può partire dal disegnare un componente grafico che abbia la possibilità di essere associato a un set di dati: di fatto ogni componente JSF che visualizzi un risultato può avere questa possibilità. Volendo essere più rigorosi possiamo dire che si tratta di componenti grafici che sono organizzati secondo il ben noto pattern MVC: in questo caso il componente visuale (la View) viene associato a un componente (il Model) che si preoccupa di mantenere la connessione con il database e di fornire il record set secondo una precisa istruzione SQL. Normalmente tale associazione avviene utilizzando gli strumenti grafici messi a disposizione del tool di sviluppo. L’esempio che andiamo brevemente ad analizzare cerca di seguire il filo logico impostato nelle puntate precedenti (applicazione Beelog, si faccia riferimento ai primi articoli della serie); per maggiori dettagli su come realizzare questa applicazione si faccia riferimento a [RAD].
Si immagini quindi che l’applicazione sia composta da una pagina JSF in cui l’utente sceglie da un menu a tendina l’apiario di cui vedere nel dettaglio i dati. Tale menu dovrà essere quindi popolato con l’elenco di tutti gli apiari presenti nel sistema (ovvero elencare i dati presenti nella tabella APIARY).
Grazie ad alcune semplici operazioni di drag and drop, possiamo associare l’oggetto dataprovider al componente grafico; a parte i “magheggi” grafici realizzati a colpi di drag&drop (non riproducibili in questo articolo) quello che accade a livello di codice è che viene creato un binding fra il componente grafico e il dataprovider definito nel backing bean della pagina; ecco la porzione di codice relativa al backing bean in cui si ricava il dataprovider:
public class Page1 extends AbstractPageBean { private void _init() throws Exception { apiaryDataProvider.setCachedRowSet( (javax.sql.rowset.CachedRowSet) getValue( "#{SessionBean1.apiaryRowSet}")); } ... }
In questo caso all’interno del metodo viene assegnata alla variabile apiaryDataProvider di tipo DataProvider il rowset prendendolo direttamente da un attributo presente in un bean di sessione: in pratica il DataProvider funzionerà come collettore di dati (rowset) senza che si debba preoccupare di come questi dati siano caricati.
Il backing bean per ricavare tale rowset utilizza le funzionalità offerte da un bean di sessione (il cosiddetto sessionBean1) che preventivamente carica tali dati nel seguente modo:
public class SessionBean1 extends AbstractSessionBean { private void _init() throws Exception { apiaryRowSet.setDataSourceName("java:comp/env/jdbc/beelog3"); apiaryRowSet.setCommand("SELECT ALL apiary.id, apiary.size, apiary.location, apiary.name, apiary.description FROM apiary"); apiaryRowSet.setTableName("apiary"); } }
Il binding fra il componente JSF e l’oggetto apiaryDataProvider che contiene i dati avviene all’interno della pagina JSF:
items="#{ Page1.apiaryDataProvider.options['apiary.id,apiary.location'] } " style="left: 192px; top: 24px; position: absolute"/>
Come si nota si fa riferimento a un datasource name che viene ricavato tramite il nome java:comp/env/jdbc/beelog3 che non è altro che un nome JNDI impostato nel deployment descriptor (non riportiamo per esigenze di spazio tali file XML ma al solito si può fare riferimento al tutorial che si trova [RAD]).
Scendere nei dettagli di questo esempio esula dagli scopi di questo articolo, per cui ci limiteremo a sintetizzare i vari passaggi che permettono al componente databound (la dropdownlist) di visualizzare i dati presenti nella tabella del database:
- L’applicazione JSF contiene un bean di sessione che si connette tramite JDBC al database e carica i dati della tabella APIARY e popola un comune oggetto Rowset. Tale oggetto verrà utilizzato per ricavare l’elenco dei record prensenti nella tabella. Il meccanismo di connessione al database fa uso della definizione di un datasource tramite un JNDI name.
- Il backing bean associato alla pagina JSF contiene un riferimento al bean di sessione, tramite il quale accede all’istanza dell’oggetto Rowset che appartiene ancora al contesto JDBC. A questo punto il backing bean effettua il travaso dei dati in un oggetto dataprovider (apparentemente al contesto JSF) che come dice il nome non ha nulla a che fare con il database (quindi JDBC) ma il cui scopo è solamente quello di fornire un set di dati al componente visual JSF.
- Infine nella pagina JSF si definisce il binding fra componente grafico e relativo dataprovider. I dati possono essere quindi visualizzati
Figura 1 – In un modello RAD puro a due livelli, i dati sono letti dal database per mezzo di una comune connessione JDBC e travasati in un componente databound. Questo oggetto si preoccupa di popolare i valori dei vari componenti JSF della GUI.
Considerazioni sull’approccio RAD puro
Il RAD puro in maniera veloce e poco costosa permette di realizzare applicazioni (GUI ma non solo). È innegabilmente un efficientissimo modo di lavorare quando si devono realizzare applicazioni di medie dimensioni in cui il tasso di crescita o di modifica sia piuttosto basso. Essendo infatti del tutto assente una infrastruttura modulare (come invece in 3Tier), in una RAD pura non si porta a fattor comune business logic che è distribuita e spezzettata in molte parti.
Quindi per ogni modifica che ci viene richiesta si rischia di dover mettere mano a una porzione importante dell’applicazione. Il costo delle modifiche e dei raffinamenti sarà sempre costante (ovvero non ammortizzo i tempi di progettazione e della prima stesura del codice). In alcuni casi può convenire rifare tutto da capo che non cercare di modificare quanto già fatto.
L’utilizzo di componenti data bound, se da un lato permette di realizzare GUI di accesso ai dati in tempi che ridicolizzano ogni altro modello multilivello, dall’altro porta ad applicazioni monoblocco (la metafora è quella dell’impianto stereo compatto, molto in voga qualche anno fa) in cui per ogni modifica è forse preferibile buttar via tutto piuttosto che correggere. Questo fatto può essere un costo che si è disposti a pagare in determinate situazioni e per certe tipologie di applicazioni, ma in un tipico scenario enterprise è spesso una via del tutto impraticabile.
RAD ibrido
L’approccio RAD puro prevede che un componente databound sia agganciabile a un dataset tramite un attore che popola il cosiddetto dataprovider: tale tipologia di oggetto, come si è avuto modo di osservare precedentemente, non è in alcun modo direttamente collegata con una sorgente di tipo database relazionale. Come dice il nome, è un “fornitore di dati” che possono arrivare anche da sorgenti differenti da quella relazionale SQL.
Forti di queste considerazioni nel modello RAD ibrido, si può procede a popolare i vari dataprovider sostituendo la connessione a una fonte JDBC, utilizzando invece un altro fornitore di dati: si potrebbe usare ad esempio un session fae’ade EJB esposto dal layer di business logic di una applicazione a tre livelli.
Tale session potrebbe restituire una lista di DTO (o semplici Value Object) ognuno dei quali faccia riferimento a una precisa entità del domain model (concetti ampiamente analizzati nelle puntate precedenti della serie): ad esempio potremmo pensare di memorizzare una lista di Apiary (entità del datamodel dell’esempio di beelog). Senza particolari sforzi concettuali il fae’ade potrebbe essere mascherato da un opportuno business delegate.
In una applicazione di questo tipo il punto di partenza potrebbe essere realizzato dalla pagina JSF in cui sia presente un pulsante cliccando sul quale si esegue il seguente codice:
public String button1_action() { // invocazione del session fae'ade tramite business delegate // per reperire la lista degli apiary con cui // popolare il datamodel List apiaryList = businessDelegate.getApiarylist(); // travasa la lista degli apiary in un ListDataModel DataModel listDM = new ListDataModel(); listDM.setWrappedData(apiaryList); this.setApiaryListDM(listDM); return null; }
In questo caso la lista dei DTO ricavata dal business delegate viene utilizzata per valorizzare la variabile ApiaryListDM, istanza della classe ListDataModel che è una specializzazione della DataModel: il suo scopo è quello di “agganciarsi” non a una lista di record set (derivanti da una interrrogazione SQL) ma piuttosto di mappare una comune lista di oggetti.
A questo punto nella pagina JSF possiamo definire il binding fra apiaryListDM e il componente grafico DataTable nel seguente modo:
…
in questo caso, invece che di un semplice dropdownmenu, si è deciso di usare il componente JSF tabella (componente DataTable della libreria HTML basic): si tratta si una tabella righe-colonne che visualizzerà in ogni riga un oggetto ApiaryDTO utilizzando i vari campi del DTO per valorizzare le relative colonne: tale corrispondenza viene definita direttamente all’interno del tag come mostrato di seguito:
Questo estratto definisce l’associazione fra una colonna della tabella e l’attributo apiaryName dell’oggetto ApiaryDTO; il valore del campo apiaryName viene utilizzato per valorizzare un oggetto text. Si noti come, in perfetto stile JSF, l’associazione avviene in maniera semplice e diretta senza alcuna formalizzazione sul tipo corrispondente (nessun type check). Il nome del campo viene qui dichiarato come stringa: se tale nome è assente o errato, non verrà generata nessuna eccezione; semplicemente non verrà stampato alcun valore in tale colonna.
Figura 2 – Il modello RAD ibrido prevede il popolamento dei dataprovider utilizando le funzionalità offerte dagli strati sottostanti. In questo caso, ad esempio, si potrebbe pensare di sfruttare l’invocazione di un session bean remoto.
Conclusioni
In questo articolo abbiamo visto sinteticamente come il framework JSF possa essere utilizzato per la produzione di un layer di presentation logic.
JSF è una tecnologia che fa largo uso del modello MVC non solo per il controllo del flusso delle pagine, ma anche per la gestione della visualizzazione dei dati nei vari componenti grafici. Ogni tabella, ogni lista, ogni campo testo può essere visto come un oggetto databound agganciabile a un corrispondente componente dataprovider.
Nel caso in cui si decida per un approccio RAD puro, tali dataprovider saranno popolati direttamente con dati caricati tramite JDBC dal database, dando vita a un classica struttura a due livelli.
Se invece il 3Tier è l’architettura scelta per l’applicazione, si potrà connettere quanto presente nei layer sottostanti (i cosiddetti session fae’ade) semplicemente mutando la modalità con la quale i vari dataprovider vengono popolati.
Nei prossimi articoli approfondiremo i concetti relativi a JSF (cercando di parlare delle differenze che ci sono fra le varie librerie di componenti) per poi passare a un rassegna delle tecnologie alternative a JSF di cui si è fatta una breve presentazione nella prima parte di questo articolo.
Riferimenti
[JSF] Alfredo Larotonda, “JSF: Il nuovo volto dello sviluppo web”, MokaByte 117, Aprile 2007
https://www.mokabyte.it/cms/article.run?articleId=XER-MML-L7M-3YX_7f000001_28844331_bc942787
[RAD] Tutorial NetBeans
http://www.netbeans.org/kb/60/web/aboutcomponents.html