Tesi di Laurea - Framework MVC per lo sviluppo di Web Application
Transcript
Tesi di Laurea - Framework MVC per lo sviluppo di Web Application
Università degli studi di Napoli Federico II Facoltà di Ingegneria Corso di Laurea in Ingegneria Informatica TESI DI LAUREA Framework MVC per lo sviluppo di Web Application : JavaServer Faces e Struts Relatore Ch.mo Prof. Antonio d’Acierno Candidato Paolo Patierno Matricola 041/2803 Anno Accademico 2004/2005 …a tutti coloro che hanno creduto in me …alla mia pazientissima famiglia …alla mia dolcissima Sara …alle mie care nonne Ringraziamenti Quando nella vita si raggiungono grandi traguardi e si realizzano i propri sogni, ci si rende conto che, senza l’appoggio delle persone che ci vivono accanto, gli sforzi e l’impegno personali non avrebbero mai consentito da soli tali risultati. Ho sempre considerato lo studio e la conoscenza tra gli elementi fondamentali della vita di una persona e per questo, per raggiungere il mio obiettivo, non ho mai lesinato nei miei confronti l’impegno, aggiungendovi costanza, instancabilità, caparbietà e tenacia che comunque sarebbero state vane senza le persone che qui ho il piacere ed il dovere di ringraziare. I miei ringraziamenti vanno innanzitutto al Prof. Ing. Antonio d’Acierno, il quale mi ha proposto questo lavoro che ho accettato con grande entusiasmo. Grazie a lui, ho avuto la possibilità di affrontare ed approfondire nuovi temi legati allo sviluppo del software per Internet, per me di estremo interesse. Gli anni di studio, che mi hanno portato a questo grande traguardo, sono stati caratterizzati da momenti di gioia così come da periodi di sacrifici ed amarezze, che non avrei saputo superare senza coloro che mi hanno preso per mano lungo questo cammino. Un immenso grazie va innanzitutto alla mia famiglia, Mamma, Papà ed ai miei fratelli Enzo e Daniela, che mi hanno dato la possibilità di realizzare questo sogno, senza mai ostacolarmi nelle decisioni ma dandomi sempre il massimo appoggio. In questi anni, hanno dovuto sopportare i miei comprensibili momenti di tensione e di nervosismo, senza mai rimproverarmi alcun comportamento. A loro devo chiedere scusa, per non aver dedicato il giusto tempo che merita una famiglia che ama il proprio figlio ed il proprio fratello. Grazie a mio cugino Diego, che rappresenta per me il modello da seguire, umanamente e professionalmente, il quale non ha mai risparmiato consigli ed elogi nei miei confronti. Spero di poter crescere nel tempo ed acquisire le notevoli capacità che gli riconosco. Grazie a tutti i miei amici più sinceri, con i quali ho trascorso delle ore spensierate, lontano dagli affanni dello studio, e che mi hanno fatto sentire una persona importante per loro. Un grazie particolare va a Sara, che ho avuto l’immensa fortuna di conoscere circa un anno fa, diventando l’amore della mia vita. Si è ritrovata “catapultata” nel mio mondo, fatto di moltissime ore si studio, di sacrifici e di preoccupazioni, dandomi sempre la forza di rialzarmi nei momenti di rabbia e di sconforto. Seppur soltanto in questo ultimo anno, il suo amore nei miei confronti è stato una grande fonte di energia, dalla quale spero di attingere per tutto il resto della mia vita. Vorrei concludere con una dedica alle mie nonne, Edy ed Anna, che ne sono certo mi hanno guidato da lassù e che in questo momento mi sono vicine, per condividere con me l’enorme gioia che ho dentro. A voi … Grazie di cuore a tutti Indice Introduzione Obiettivo della tesi…………………………………………………… 1 Capitolo I – Il pattern MVC 1. Architettura software “a livelli” …………………………………. 3 2. L’importanza dei “design patterns” …………………………….... 6 3. Design Pattern MVC Model – View – Controller ………………... 8 4. Evoluzione dell’architettura delle Web Application ………………… 11 4.1 Model 1 (Page – Centric Architecture) ………………………… 12 4.2 Model 2 (Servlet – Centric Architecture) ………………………. 13 5. Le Servlet …………………………………………………….. 15 5.1 Struttura di base …………………………………………… 16 5.2 Ciclo di vita ………………………………………………... 17 5.3 Richiesta, Risposta, Sessione e Contesto ………………………... 19 6. Web Application Frameworks …………………………………… 21 7. Struttura di una Web application in Java …………………………. 22 7.1 Web Application Deployment Descriptor ………………………. 23 Capitolo II – Il framework JavaServer Faces 1 . Introduzione ………………………………………………….. 26 2. Teamworking …………………………………………………. 29 3. Modelli architetturali - Framework Models ………………………. 31 3.1 Execution Model …………………………………………… 32 I 3.1.1 FacesServlet ……………………………………………. 32 3.1.2 Lifecycle – PhaseListener ………………………………… 33 3.1.3 Application …………………………………………….. 34 3.1.4 FacesContext – ExternalContext …………………………. 35 3.2 User Interface Component Model ……………………………… 37 3.3 Component Rendering Model …………………………………. 40 3.3.1 Renderer ………………………………………………... 40 3.4 Conversion Model ……………………………………………. 42 3.4.1 Converter ……………………………………………….. 43 3.5 Event and Listener Model ……………………………………. 44 3.5.1 FacesEvent ……………………………………………… 45 3.5.2 FacesListener ……………………………………………. 45 3.6 Validation Model …………………………………………… 46 3.6.1 Validator ………………………………………………. 47 3.7 Navigation Model …………………………………………… 48 3.8 Backing Bean Management …………………………………... 49 4. Ciclo di vita di una pagina JavaServer Faces ……………………….. 52 4.1 Scenari di elaborazione di una richiesta ………………………… 53 4.2 Ciclo di vita Standard ……………………………………….. 55 4.2.1 Restore View …………………………………………… 56 4.2.2 Apply Request Values …………………………………… 56 4.2.3 Process Validation ………………………………………. 57 4.2.4 Update Model Values …………………………………… 57 4.2.5 Invoke Application ……………………………………… 58 4.2.6 Render Response ………………………………………… 58 II 5. JSF Expression Language ……………………………………… 59 6. Espandibilità del framework ……………………………………. 60 6.1 Custom Converter …………………………………………... 60 6.2 Event Listener ……………………………………………... 61 6.2.1 Implementazione di un ActionListener/ValueChangeListener ... 62 6.2.2 Backing Bean Method Listener …………………………… 63 6.3 Custom Validator …………………………………………... 63 6.3.1 Implementazione di un Validator …………………………. 64 6.3.1.1 Class Validator ……………………………………... 65 6.3.1.2 Tag Handler ………………………………………... 65 6.3.1.3 Tag Library Descriptor ………………………………. 66 6.3.2 Backing Bean Method Validator ………………………….. 66 6.4 Custom UI Components ……………………………………... 67 6.4.1. Class Component ……………………………………….. 69 6.4.2 Tag Handler ……………………………………………. 71 6.4.3 Tag Library Descriptor …………………………………… 72 6.4.4 Classe Renderer ………………………………………….. 73 7. Sicurezza ……………………………………………………… 73 8. Configurazione dell’applicazione ………………………………….. 74 8.1 Configurazione dei Backing Beans ……………………………... 75 8.2 Localizzazione, Internazionalizzazione e Messaggi ………………. 76 8.3 Registrare un Custom Validator ………………………………. 77 8.4 Registrare un Custom Converter ………………………………. 77 8.5 Configurare le Navigation Rules ………………………………. 78 8.6 Registrare un Custom Renderer in un Renderer Kit ………………. 79 III 8.7 Registrare un Custom Component ……………………………. 80 Capitolo III – Il framework Struts 1 . Introduzione ………………………………………………….... 81 2. Controller, Model and View Components ………………………….. 83 2.1 Controller Components ……………………………………….. 84 2.1.1 ActionServlet ……………………………………………. 85 2.1.2 RequestProcessor …………………………………………. 86 2.1.3 Action ………………………………………………….. 88 2.1.3.1 ForwardAction ………………………………………. 89 2.1.3.2 DispatchAction – LookupDispatchAction ………………. 90 2.1.3.3 SwitchAction ………………………………………… 91 2.1.4 ActionForward ………………………………………….. 91 2.2 Utility Classes ………………………………………………. 92 2.3 Model Components …………………………………………... 93 2.4 View Components …………………………………………… 94 2.4.1 ActionForm …………………………………………….. 95 2.4.2 ActionErrors …………………………………………… 97 2.4.2.1 ActionMessage – ActionError ………………………… 98 2.4.3 DynaActionForm ……………………………………….... 99 2.4.4 Tag Libraries …………………………………………….100 3. Ciclo di vita di una richiesta …………………………………….. 101 4. Exception Handling ………………………………………….... 102 4.1 Approccio Dichiarativo ………………………………………103 4.2 Approccio programmatico …………………………………......104 IV 4.3 ModuleException …………………………………………....104 5. Internazionalizzazione (I18N) …………………………………..105 6. JavaServer Pages Standard Tag Library (JSTL) …………………...107 7. Estensioni con i PlugIn ………………………………………….108 8. Validator framework……………………………………………109 9. Tiles framework ………………………………………………..111 9.1 Definitions …………………………………………………113 9.2 Costruzione di una pagina ……………………………………114 10. Sicurezza …………………………………………………….115 11. Configurazione dell’applicazione…………………………………116 11.1 DataSource ……………………………………………….117 11.2 FormBean ………………………………………………...118 11.3 Global Exceptions …………………………………………120 11.4 Global Forwards …………………………………………..121 11.5 Action Mapping …………………………………………...121 11.6 Controller ………………………………………………....123 11.7 Message resources …………………………………………..123 11.8 Plug-In …………………………………………………...124 Capitolo IV – I framework a confronto 1. Introduzione ……………………………………………………..125 2. Ciclo di vita di una richiesta ……………………………………….127 3. Controller Tier …………………………………………………...130 4. Interfaccia Utente (UI) ……………………………………………133 5. Events e Listeners …………………………………………….......136 V 6. Mappatura delle richieste sulla Business-Logic ………………………..140 7. Conversione ……………………………………………………...144 8. Validazione ……………………………………………………..145 9. Navigazione ……………………………………………………..148 10. Expression Language …………………………………………....150 11. Eccezioni ………………………………………………………152 12. Internazionalizzazione (I18N) …………………………………...154 13. Sicurezza ………………………………………………………155 14. Configurazione ………………………………………………….156 15. Web Application Layout ………………………………………...157 16. Migrazione da Struts a JavaServer Faces …………………………..159 16.1 Strategie di migrazione ……………………………………...159 16.1.1 Components Only ………………………………………159 16.1.2 Incremental migration …………………………………...161 16.1.3 Full migration ……………………………………….....164 Capitolo V – Case Study : Analisi e Progettazione 1. Introduzione ……………………………………………………..165 2. Requisiti ………………………………………………………...167 3. Progettazione …………………………………………………….168 3.1 Use Case Diagrams …………………………………………168 3.1.1 Generale ………………………………………………..169 3.1.2 Gestione Scheda Prepagata ………………………………..170 3.1.3 Registrazione ……………………………………………171 3.1.4 Azioni su Brani ……………………………………........171 VI 3.1.5 Gestione Playlist ………………………………………....172 3.1.6 Gestione Utenti ………………………………………….173 3.1.7 Gestione Archivio Brani …... ………………………...........174 3.1.8 Casi d’uso comuni : Login, Logout, Gestione Dati Personali ......176 3.2 Class Diagram ……………………………………………...177 3.3 Activity Diagrams – Sequence Diagrams …………………….....180 3.3.1 Login …………………………………………………..182 3.3.2 Logout …………………………………………………187 3.3.3 Registrazione ……………………………………………189 3.3.4 Richiesta Username / Password …………………………...192 3.3.5 Acquisto, Ricarica e Visualizzazione Info Scheda Prepagata ….193 3.3.6 Visualizzazione e Ricerca Brani ….... ……………………..199 3.3.7 Inserimento, Modifica ed Eliminazione Brano dall’Archivio .......202 3.3.8 Ascolto di un singolo Brano ……………………………….211 3.3.9 Gestione Playlist ………………………………………....214 3.3.10 Acquisto/Download Brano ……………………………...226 3.3.11 Modifica Dati Personali ………………………………....229 3.3.12 Gestione Utenti ………………………………………...234 3.4 Statechart Diagrams ………………………………………....237 3.5 Conceptual Data Model ……………………………………...244 3.6 Physical Data Model ………………………………………...247 Capitolo VI – Case Study : Implementazioni a confronto 1. Introduzione ……………………………………………………..252 2. Struttura dell’applicazione ………………………………………....254 VII 3. Controller …………………………………………………..........263 3.1 Gestione delle sessioni ………………………………………...264 3.2 Protezione pagine ……………………………………………266 4. View …………………………………………………………....268 4.1 Le librerie Standard …………………………………………269 4.1.1 I form : contenuto e layout ……………………......................270 4.1.2 Costruzione condizionale dei contenuti ……………………....272 4.1.3 DataTable JSF e Cicli JSTL ……………………………..273 4.2 Tiles ……………………………………………………….275 4.3 JavaServer Faces Custom Components ……………………….....278 4.3.1 DateSelectComponent …………………………………….279 4.3.2 DDListComponent ……………………………….............283 4.3.3 TimeSelectComponent ……………………………..............285 4.3.4 …e Struts ? …………………………………………….287 4.4 I formbean di Struts …………………………………………288 4.4.1 Login, Registrazione e Richiesta password …………………..289 4.4.2 Acquisto/Ricarica scheda prepagata ………………………..292 4.4.3 Modifica dati utente ed amministratore ………………….......294 4.4.5 Visualizzazione e ricerca dei brani ……………………........294 4.4.6 Inserimento/Modifica brani ……………………………….297 4.4.7 Gestione Playlist ………………………………………....298 4.4.8 Gestione Utenti ………………………………………….300 4.4.9 … e JavaServer Faces ? …………………………………..301 4.5 Player MP3 ………………………………………………...302 4.6 Internazionalizzazione (I18N) ……………………………......303 VIII 5. Validazione ……………………………………………………..306 5.1 JavaServer Faces Custom Validators …………………………..306 5.1.1 EmailValidator ………………………………………....308 5.1.2 SeqCifreValidator ……………………………………….310 5.1.3 TelefonoValidator ……………………………………......311 5.2 Plug-in Validator Struts ……………………………………..313 6. Model …………………………………………………………..316 6.1 Classi Exception ……………………………………………317 6.2 Classi di interfacciamento con il DataBase ……………………...318 6.3 Le Action di Struts ………………………………………….329 6.4 Business-Logic e funzionalità del sistema ……………………….344 6.4.1 Package accesso ………………………………………….346 6.4.2 Package ruolo …………………………………………...348 6.4.3 Package brani …………………………………………...357 6.4.4 Package playlist ……………………………………….....364 6.4.5 Package scheda ………………………………………......370 6.4.6 Package cartacredito ……………………………………...374 6.4.7 Package download ……………………………………….376 6.4.8 Package mail ……………………………………………376 6.5 I Backing Beans di JavaServer Faces …………………………..377 7. Plug-in di Struts …………………………………………………380 8. Navigazione ……………………………………………………..382 9. Eccezioni ………………………………………………………..384 10. Sicurezza ………………………………………………………385 IX Appendice A – SRS Web Application MP3-Web……………………...390 Riferimenti Bibliografici ………………………………………………...426 X Introduzione – Obiettivo della tesi _________________________________________________________________ Introduzione Obiettivo della tesi Il lavoro di tesi svolto si pone come obiettivo il confronto tra due dei principali framework MVC per lo sviluppo di Web application : JavaServer Faces e Struts. Considerando il linguaggio comune su cui si basano, ossia il Java, tale confronto è relativo soprattutto a ciò che riguarda gli strumenti, le funzionalità e le potenzialità fornite dall’uno e dall’altro. Si parte da una descrizione del pattern MVC (Model – View – Controller) e di tutti gli aspetti relativi al suo meccanismo di funzionamento, nonché da una spiegazione delle Servlet che sono alla base dei framework suddetti. Successivamente, viene dato ampio spazio al framework JavaServer Faces descrivendone tutte le classi che ne costituiscono l’architettura e tutte le funzionalità messe a disposizione per realizzare un’applicazione Web. Allo stesso modo è stata approfondita la struttura di Struts ed i relativi strumenti disponibili. Raccolte le informazioni necessarie, è stato realizzato un confronto teorico tra i due framework evidenziando vantaggi e svantaggi dell’uno e dell’altro in relazione a ciascuna funzionalità offerta. Per poter fornire una base solida al confronto teorico, è stato preso in esame un caso di studio che ha previsto la realizzazione di una Web application mediante i due framework. Tale applicazione mette a disposizione degli utenti la possibilità di usufruire di contenuti audio, ossia brani MP3, registrandosi, creando delle proprie playlist per poterle ascoltare in streaming e per poter eventualmente acquistare i brani ed effettuarne il download. Per quanto riguarda l’amministratore, egli ha a disposizione tutte le funzionalità di gestione dell’archivio dei brani e degli utenti registrati. E’ stata eseguita una preventiva fase di analisi e progettazione che ha previsto la realizzazione del documento di specifica dei requisiti (SRS) e di tutti i 1 Introduzione – Obiettivo della tesi _________________________________________________________________ diagrammi UML (Use Case, Class, Activity, Sequence, Statechart) oltre al modello concettuale e fisico della base di dati necessaria. A queste fasi ha fatto seguito una doppia fase di implementazione che ha previsto la realizzazione della Web application con entrambi i framework. Infine, sulla base di quanto sviluppato sono state ripresi tutti gli aspetti che potessero rappresentare termini di confronto fra di essi e sono state evidenziate le analogie e le differenze di implementazione dell’uno e dell’altro. Per quanto concerne gli strumenti adottati si è fatto ampio uso del software Sybase Power Designer per lo sviluppo dei diagrammi UML, dell’ambiente IDE Eclipse 3.1 ed i plug-in Exadel ed Omondo per l’implementazione ed infine del Web Container Apache Tomcat 5.0.28 come ambiente di esecuzione delle due implementazioni. 2 Capitolo I – Il Pattern MVC _________________________________________________________________ Capitolo I – Il Patter n MVC 1. Architettura software “a livelli” Nello sviluppo delle applicazioni software, è possibile descrivere l’architettura del sistema utilizzando uno fra i molteplici paradigmi a disposizione, ma in linea generale, trova una maggiore applicazione la nota architettura “a livelli” (Layered Application Architecture). Quest’ ultima prevede che un sistema software sia decomposto in tre livelli nettamente distinti, che comunque abbiano la possibilità di comunicare fra loro secondo un’opportuna gerarchia. Ciascuno dei tre livelli ha un proprio ruolo ed assolve ad uno specifico compito all’interno del sistema complessivo, senza interferire con gli altri livelli ma scambiando con essi le informazioni necessarie all’esecuzione di elaborazioni anche molto complesse. I tre livelli in questione sono i seguenti : - Presentation layer : è il livello di presentazione, il cui compito è quello di interagire direttamente con l’utente del sistema, acquisire i dati di input immessi da quest’ultimo e visualizzare i risultati dell’elaborazione effettuata dal sistema stesso. Esso, in pratica, definisce la GUI (Graphic User Interface) ossia l’interfaccia grafica dell’applicazione; - Application processing layer : è il livello in corrispondenza del quale si trova la “business-logic” dell’applicazione e quindi tutti i moduli software che implementano le funzionalità che il sistema mette a disposizione. In sostanza, è il centro dell’elaborazione dei dati in cui avvengono tutte le computazioni; - Data management layer : è il livello che si occupa della gestione della persistenza e dell’accesso ai dati, per cui è tipicamente caratterizzato da un DBMS (DataBase Management System); 3 Capitolo I – Il Pattern MVC _________________________________________________________________ Figura 1 – Architettura Software “a livelli” Sviluppando un’applicazione secondo questa architettura, ogni livello è indipendente dagli altri, per cui la modifica di uno di essi non ha effetto sui restanti. Tuttavia è prevista la comunicazione fra loro e lo scambio di informazioni. Un tipico scenario di funzionamento del sistema può essere il seguente : un utente utilizza l’applicazione, interagendo direttamente con la GUI e fornisce quindi al Presentation layer, i dati su cui andrà eseguita l’elaborazione. Il Presentation layer, acquisiti i dati di input, li trasferisce all’Application processing layer che esegue su di essi una determinata computazione. Durante l’elaborazione, la business-logic può prevedere la memorizzazione persistente dei dati oppure la necessità di acquisire ulteriori dati già memorizzati. In questo caso, c’è l’interazione con il Data managemente layer, il quale memorizza i dati che gli vengono passati dal livello superiore, oppure recupera da un Data Source (es. database) i dati richiesti e li trasmette alla business-logic. Al termine dell’elaborazione i risultati vengono passati al Presentation layer che li visualizza in una certa forma all’utente finale. Facendo riferimento al paradigma Client-Server, notevolmente utilizzato nelle Web application ma di gran richiamo anche per applicazioni desktop, i tre livelli del sistema devono essere correttamente ripartiti anche da un punto di vista hardware. Le principali architetture per la ripartizione sono : - Two-Tier nelle due soluzioni Thin e Fat Client; - Three-Tier; 4 Capitolo I – Il Pattern MVC _________________________________________________________________ L’architettura Two-Tier prevede un unico Client ed un unico Server ed i tre livelli dell’applicazione software sono distribuiti fra di essi secondo due possibili modalità : - Thin Client : sul Client risiede il Presentation layer mentre sul Server gli altri due livelli (Application processing layer e Data management layer). Un vantaggio può risiedere nel fatto che una modifica alla business-logic va eseguita una sola volta sul Server, mentre lo svantaggio principale può essere caratterizzato dall’enorme carico di lavoro che deve supportare il Server stesso dato il numero elevato di Client che possono accedere ad esso; - Fat Client : sul Client risiedono i primi due livelli (Presentation layer e Application processing layer) mentre sul Server soltanto il Data management layer. Il vantaggio è quello di ridurre il carico di lavoro sul Server che si occupa solo dell’accesso ai dati, delegando l’elaborazione degli stessi al Client. Lo svantaggio principale è la complessità maggiore dei Client e quindi la necessità di aggiornare ciascuno di essi nel caso in cui vengano apportate modifiche alla business-logic; Figura 2 – Architetture Two Tier L’architettura Three-Tier, maggiormente utilizzata, prevede la presenza di un unico Client ed una coppia di Server. Sul Client risiede il Presentation layer e su ciascuno dei due Server sono distribuiti i due restanti livelli (Application processing layer e Data management layer). Nell’ambito di una Web application, il Client è caratterizzato da un nodo della rete sul quale è in esecuzione il browser, mentre i due Server, da un punto di vista software, sono tipicamente inglobati in un unico nodo 5 Capitolo I – Il Pattern MVC _________________________________________________________________ della rete che funge da Server fisico. In particolare, sulla stessa macchina sono in esecuzione il Web Server associato all’Application proccessing layer ed il Database Server associato al Data management layer. Figura 3 – Architettura Three Tier In conclusione, è proprio su questa particolare architettura “a livelli” che si basa il pattern MVC (Model – View – Controller) , che rappresenta il fondamento dei framework Struts e JavaServer Faces (JSF) che saranno oggetto di studio e di un approfondito confronto. 2. L’importanza dei “design patterns” Alla metà del ventesimo secolo, l’architetto Christopher Alexander osservò come i suoi colleghi tendevano a risolvere i medesimi problemi, più o meno allo stesso modo. Da tale osservazione, introdusse il concetto di “design pattern”, ovviamente riferito all’architettura. Secondo Christopher Alexander, un design pattern “descrive un problema che si presenta frequentemente nel nostro ambiente, e quindi descrive il nucleo della soluzione in modo tale che sia possibile impiegare tale soluzione milioni di volte, senza peraltro produrre due volte la stessa realizzazione”. Ovviamente, tale definizione era riferita all’ambito architetturale, ma nel 1994, con il libro “Design Patterns : Elements of Reusable Object-Oriented Software”, Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, applicarono questa intuizione allo sviluppo del software. In questo caso, il principio è ugualmente valido anche se riferito ad oggetti, classi ed interfacce piuttosto che ad elementi architettonici come muri, archi e pilastri. 6 Capitolo I – Il Pattern MVC _________________________________________________________________ Un pattern è un modello che permette di definire la “soluzione” di un “problema” specifico che si ripresenta, di volta in volta, in un “contesto” diverso. Presenta inoltre le seguenti caratteristiche : - il “nome” che individua il pattern e la cui importanza non è secondaria, perché rientra a far parte del vocabolario dello sviluppo software; - la descrizione del “problema” in maniera dettagliata, con la sua struttura e le condizioni al contorno; - la “soluzione” che descrive gli artefatti software per risolvere il problema come gli elementi che rientrano nello sviluppo, quali le classi e le relazioni fra esse, le associazioni ed i ruoli, le modalità di collaborazione tra le classi coinvolte ed infine la distribuzione delle responsabilità nella soluzione del particolare problema di design considerato; - le “conseguenze” che descrivono i risultati che si possono ottenere dall’applicazione del pattern per spingere uno sviluppatore a farne uso; Ad oggi i patterns sono di grande interesse, in virtù del fatto che rappresentano una successiva evoluzione del paradigma OOP (Object – Oriented Programming), poiché combinano classi ed oggetti atomici per fornire una base più ampia per la risoluzione di un problema, nella fattispecie per lo sviluppo di un’applicazione software. Sotto questo punto di vista, un design pattern fornisce allo sviluppatore : - una soluzione codificata e consolidata per un problema ricorrente; - un’astrazione di granularità e livello di astrazione più elevati di una classe; - un supporto alla comunicazione delle caratteristiche del progetto; - un modo per progettare software con caratteristiche predefinite; - un supporto alla progettazione di sistemi complessi; - un modo per gestire la complessità del software; Può essere considerato un “buon” pattern un pattern che descrive una soluzione “assodata” per un problema “ricorrente” in un contesto “specifico”. 7 Capitolo I – Il Pattern MVC _________________________________________________________________ E’ ovvio che esistono numerose tipologie di design patterns, ma nell’ambito dello sviluppo delle Web application, in riferimento all’obiettivo proposto, assume un ruolo rilevante il pattern MVC (Model View Controller). 3. Design Pattern MVC Model – View – Controller Il design pattern MVC ha le sue origini nell’ambiente Smalltalk, in cui veniva utilizzato per la realizzazione della GUI (Graphic User Interface) di applicazioni desktop e non orientate al Web. Tale pattern si basa sull’idea di separare i dati dalla rappresentazione, poiché mantenere un forte accoppiamento tra essi comporta che la modifica dell’uno, implica automaticamente un’ aggiornamento dell’altro. Esso, quindi, prevede che un sistema software sia realizzato secondo l’architettura “a livelli”, stabilendo un disaccoppiamento fra dati e rappresentazione, mediante la definizione di tre elementi noti come : Model, View e Controller. Figura 4 - Model, View e Controller Il Model (Modello) è responsabile della gestione dei dati e del comportamento dell’applicazione (data & behaviour). Esso coordina la “businesslogic” dell’applicazione, l’accesso alle basi di dati e tutte le parti critiche “nascoste” del sistema. Incapsula lo stato dell’applicazione ed espone le funzionalità di 8 Capitolo I – Il Pattern MVC _________________________________________________________________ quest’ultima. E’ indipendente dalle specifiche rappresentazioni dei dati sullo schermo e dalle modalità di input dei dati stessi da parte dell’utente. Ad esso fanno riferimento l’Application processing layer ed il Data managemente layer nel design del software “a livelli”. Il Model può essere scomposto in tre sottolivelli puramente concettuali : - External interface : caratterizzato dal codice che definisce un’interfaccia mediante la quale il codice esterno comunica con il Model. Generalmente il codice esterno è determinato dal framework adottato per sviluppare la Web application, come ad esempio Struts e JavaServer Faces; - Business logic : rappresenta il cuore del Model contenente il codice che realizza le funzionalità dell’applicazione; - Data access : costituito dal codice che permette di accedere ad un datasource, come ad esempio una base di dati; Figura 5 – Architettura del Model I tre sottolivelli descritti non rappresentano necessariamente dei set di classi separate ma, bensì, dei set di responsabilità differenti che ha il Model. Lo sviluppatore può decidere di realizzare i tre sottolivelli mediante una o più classi e raggruppando due o più sottolivelli. Tipicamente si preferisce realizzare il sottolivello Data access con una 9 Capitolo I – Il Pattern MVC _________________________________________________________________ o più classi che hanno come unico scopo quello di permettere l’accesso ad un base di dati. Gli altri due sottolivelli possono essere realizzati in due modi : - separazione : ci sono una serie di classi che realizzano la Business logic ed ulteriori classi che definiscono l’External interface; - fusione : sono definite una serie di classi che definiscono la Business logic e contengono all’interno anche le funzionalità di interfacciamento dell’External interface; La scelta può dipendere dalla complessità dell’applicazione ed , eventualmente, dalla tecnologia che viene adottata sul Model per realizzare la Web application. La View (Vista) ha il compito di visualizzare i dati e presentarli all’utente anche in forme diverse, in relazione al dispositivo utilizzato per accedere al sistema (es. personal computer, cellulare, ..) . Ciò vuol dire che, pur partendo dagli stessi dati, è possibile effettuare “rendering” diversi ed ottenere viste multiple dello stesso modello. Ad esso fa riferimento il Presentation layer. Il Controller (Controllo) definisce il meccanismo mediante il quale il Model e la View comunicano. Realizza la connessione logica tra l’interazione dell’utente con l’interfaccia applicativa e i servizi della business-logic nel back-end del sistema. E’ responsabile della scelta di una tra molteplici viste dello stesso modello, in base al tipo di dispositivo utilizzato dall’utente per accedere al sistema ma anche in relazione alla localizzazione geografica dell’utente stesso. Una qualsiasi richiesta (request) fatta al sistema viene acquisita dal Controller che individua all’interno del Model il gestore della richiesta (request handler). Ottenuto il risultato dell’elaborazione (response), il Controller stesso determina a quale View passare i dati per la presentazione degli stessi all’utente. Il vantaggio principale che scaturisce da questa architettura, è che la business-logic definita all’interno del Model è separata dal Presentation layer che si trova all’interno della View. Tutto ciò favorisce il riuso dei componenti e la possibilità di apportare delle modifiche ad un livello senza avere degli effetti sull’altro. 10 Capitolo I – Il Pattern MVC _________________________________________________________________ 4. Evoluzione dell’architettura delle Web Application Considerando il Java come uno dei migliori linguaggi per lo sviluppo delle applicazioni Web, attraverso l’uso delle Servlets e delle pagine JSP (Java Server Pages), l’architettura delle Web application ha subito una notevole evoluzione nel corso degli anni, seguendo un iter di questo tipo : 1. Assenza del pattern MVC; 2. Utilizzo del pattern MVC secondo il Model 1 (Page – Centric); 3. Utilizzo del pattern MVC secondo il Model 2 (Servlet – Centric); 4. Web application Frameworks (es. Struts); 5. Web application Framework basato su uno standard ( JavaServer Faces – JSR 127); Tale evoluzione ha previsto un aumento della complessità e della robustezza di ciascuna applicazione e può essere schematizzata nel modo seguente, sino all’introduzione del Model 1 : Figura 6 - Evoluzione dello sviluppo di Web Application 11 Capitolo I – Il Pattern MVC _________________________________________________________________ 4.1 Model 1 (Page – Centric Architecture) Il Model 1 del pattern MVC è detto anche Page – Centric, poiché l’architettura della Web application è basata sulle pagine JSP. Il sistema complessivo è composto da una serie di pagine JSP legate fra loro, ciascuna delle quali gestisce tutti gli aspetti principali dell’applicazione tra cui la presentazione, il controllo ed i processi di business. E’ evidente che la business-logic ed il controllo sono fortemente accoppiati all’interno di ciascuna pagina JSP, attraverso l’utilizzo di JavaBeans, Scriptlets ed Espressioni. I tre elementi del pattern, Model, View e Controller, pur essendo distinti, sono inglobati all’interno di una stessa struttura che in questo caso è rappresentata da una pagina JSP. Figura 7 - MVC Model 1 (Page - Centric Architecture) Il modello prevede uno scenario di interazione di questo tipo : Il browser invia una richiesta (request) di una risorsa al Web Server, nella maggior parte dei casi per la visualizzazione di una pagina JSP. Il Web Server, tipicamente, funge da Web Container e quindi da Servlet Container in quanto va ricordato che una pagina JSP, una volta richiesta, viene sempre trasformata in una corrispondente Servlet. All’interno della pagina JSP, ci sono i tag che ne definiscono la presentazione all’utente e quindi l’aspetto grafico (GUI) , ma anche gli elementi per l’esecuzione 12 Capitolo I – Il Pattern MVC _________________________________________________________________ delle elaborazioni. Queste ultime possono essere eseguite all’interno della pagina stessa, attraverso codice Java immerso nei tag (Scriptlets) oppure mediante dei componenti esterni, ai quali si fa riferimento da tale pagina. I componenti in questione sono tipicamente dei JavaBeans, i quali effettuano una qualsiasi computazione, comunicando eventualmente con il back-end del sistema, ad esempio per l’accesso a basi di dati. Il risultato di ciascuna elaborazione sarà così integrato all’interno della pagina HTML prodotta, che sarà inviata nella risposta (response) al browser. Da quanto detto, si evince che Model, View e Controller sono praticamente integrati all’interno di ciascuna pagina JSP e che non c’è una netta separazione fra essi. I limiti principali di questo modello possono essere i seguenti : - viene incoraggiata una struttura a “spaghetti” delle pagine JSP, poiché la business-logic si “perde” all’interno di ciascuna pagina e la navigazione nella Web application complessiva viene fatta pagina per pagina; - è molto difficile eseguirne il debug, poiché tutti gli errori riportati dal Web Container, fanno riferimenti al codice compilato della pagina JSP in una Servlet e quindi sono difficili da individuare all’interno della pagina stessa; Tutto ciò rappresenta un primo passo verso il modello definitivo del pattern MVC. 4.2 Model 2 (Servlet – Centric Architecture) Il Model 2 del pattern MVC è detto anche Servlet – Centric, poiché l’architettura della Web application si basa fortemente sull’utilizzo di una Servlet. Il sistema complessivo è composto da una Servlet principale che svolge il ruolo di Controller, da una serie di pagine JSP che rappresentano la View ed infine da un insieme di JavaBeans che costituiscono il Model. I tre elementi del pattern MVC sono quindi nettamente separati tra loro, pur mantenendo la possibilità di comunicare per scambiarsi informazioni. In particolare, le pagine JSP si occupano esclusivamente della presentazione dei dati all’utente, senza contenere un minimo di business-logic. La Servlet “master” ha il compito di acquisire tutte le richieste provenienti dalla rete e funge da “dispatcher”, inoltrando ciascuna di esse verso il 13 Capitolo I – Il Pattern MVC _________________________________________________________________ corrispondente “handler” per permetterne la gestione. Al termine dell’elaborazione, è la stessa Servlet che determina a quale pagina JSP restituire il controllo, eseguendo il “redirecting”. Infine, i JavaBeans incapsulano le funzionalità dell’applicazione, interagiscono con il back-end del sistema ed eseguono tutte le elaborazioni richieste. Su tale modello, si basano i framework Struts e JavaServer Faces (JSF). Figura 8 - MVC Model 2 (Servlet - Centric Architecture) Il modello prevede uno scenario di interazione di questo tipo : Il browser invia una richiesta (request) di una risorsa al Web Server, nella maggior parte dei casi per la visualizzazione di una pagina JSP. Il Web Server, tipicamente funge da Web Container e quindi da Servlet Container, in quanto è in esecuzione su di esso, la “master” Servlet della Web application. Quest’ultima funge da Controller ed acquisisce la richiesta, sulla base della quale individua l’handler che dovrà gestirla. In particolare, vengono istanziati una serie di JavaBeans, costituenti il Model, che eseguono le elaborazioni richieste ed eventualmente interagiscono con il back-end del sistema, accedendo ad una base di dati. Al termine della computazione, il controllo ritorna alla Servlet che sulla base del risultato, determina la View e quindi la pagina JSP verso la quale eseguire il redirecting, la quale estrae dai JavaBeans i risultati e li visualizza all’utente. Infine, la pagina HTML prodotta viene trasmessa al browser come risposta (response) definitiva. 14 Capitolo I – Il Pattern MVC _________________________________________________________________ Da quanto detto si evince che Model, View e Controller sono nettamente separati fra loro e che il Controller funge da tramite per la comunicazione tra i primi due. Da ciò, scaturisce anche che il debug può essere eseguito in maniera piuttosto semplice su ciascun JavaBeans, estrapolandolo dal sistema complessivo. Per quanto riguarda la struttura del Controller, è possibile pensare di utilizzare una sola Servlet come detto sino ad ora, oppure più Servlets. La scelta dipende dal livello di granularità della Web application ed è possibile pensare a tre diverse soluzioni : - una sola “master” Servlet; - una Servlet per ciascun caso d’uso dell’applicazione oppure per ogni macrofunzionalità offerta; - combinazione delle due precedenti soluzioni : una “master” Servlet per gestire le funzioni comuni a tutti gli ambiti dell’applicazione, che delega alle Servlets “figlie” la gestione di particolari macrofunzionalità del sistema; 5. Le Servlet L’elemento tecnologico che ha determinato un notevole miglioramento nello sviluppo delle Web application e che si pone alla base dei framework MVC, è la Servlet. Quest’ultima, per definizione, è sostanzialmente una classe Java orientata alla comunicazione tra client e server. Essa riceve dei messaggi di richiesta da parte del client e produce, in corrispondenza, dei messaggi di risposta. E’ in esecuzione all’interno di un Web container secondo un particolare ciclo di vita per definito e può essere di due tipi : - Servlet generica : non è orientata alla comunicazione con uno specifico protocollo; - Servlet HTTP : orientata alla comunicazione Client-Server basata su protocollo HTTP; 15 Capitolo I – Il Pattern MVC _________________________________________________________________ Figura 9 - Classi Servlet Essendo lo studio orientato ai framework MVC, assumono un ruolo fondamentale le Servlet HTTP, delle quali vanno evidenziate gli elementi costitutivi ed i princìpi di funzionamento. 5.1 Struttura di base La parte fondamentale di una Servlet è caratterizzata dai suoi metodi di servizio, che vengono invocati dal Web container, in cui è in esecuzione la Servlet stessa, per gestire le richieste HTTP che le pervengono. Sono stati pensati in modo tale che si abbia la possibilità di poter gestire in modo differenziato le richieste che sono state inviate con metodi HTTP diversi, quali POST e GET. Tali metodi di servizio sono sostanzialmente i seguenti : - doGet() : contiene le operazioni da eseguire per rispondere ad una richiesta inviata con metodo GET; - doPost() : contiene le operazioni da eseguire per rispondere alle richieste inviate con metodo POST; Quando si realizza una propria Servlet, estendendo la classe HTTPServlet, è necessario eseguire l’overriding di questi due metodi e , nel caso in cui una richiesta debba essere gestita allo stesso modo indipendentemente che sia di tipo POST oppure GET, è possibile scrivere il codice in uno dei due metodi e fare in modo che l’altro lo invochi. 16 Capitolo I – Il Pattern MVC _________________________________________________________________ Essi prevedono una signature particolare, in quanto i parametri caratteristici sono : - request : oggetto HTTPServletRequest che contiene le informazioni della richiesta da gestire; - response : oggetto HTTPServletResponse che conterrà la risposta da inoltrare verso il client; 5.2 Ciclo di vita Una volta che la Servlet è stata realizzata dallo sviluppatore, non è quest’ultimo che si occupa di istanziarla al momento opportuno ma è il Web container, che ne gestirà il corrispondente ciclo di vita. Il programmatore non dispone nemmeno di un riferimento alla Servlet ed ai suoi metodi, per cui non può invocarli. Questi ultimi sono stati pensati per la gestione di particolari eventi che in questo caso sono delle richieste HTTP. Ciò vuol dire che allo scatenarsi dell’evento “richiesta HTTP” giunta al Web container ed indirizzata ad una certa Servlet, viene avviato automaticamente uno dei due metodi doGet() e doPost() per la sua gestione. Il ciclo di vita di un oggetto Servlet prevede le seguenti fasi : - inizializzazione : il contenitore crea un’istanza della Servlet; - servizio : l’istanza viene utilizzata per gestire le richieste; - distruzione : l’istanza viene deallocata e rimossa dal Web container; Figura 10 - Ciclo di vita di una Servlet 17 Capitolo I – Il Pattern MVC _________________________________________________________________ Nella fase di inizializzazione, il Web container invoca il metodo init() della Servlet, il quale può essere soggetto ad overriding qualora si rendesse necessario effettuare particolari operazioni di inizializzazione, come ad esempio l’azzeramento di contatori. A questo punto, è stato creato un processo associato alla Servlet residente in memoria e quest’ultima è pronta per ricevere e gestire le richieste. Nel momento in cui giunge una richiesta, il Web container crea un thread associato alla Servlet e su di esso invoca il metodo service(), il quale a sua volta non fa altro che invocare il metodo doGet() oppure doPost() passandogli gli oggetti relativi alla request ed alla response. La caratteristica peculiare delle Servlet è che non viene allocato un nuovo processo per ogni richiesta ma bensì un thread, per cui c’è un notevole risparmio di risorse rispetto alla gestione prevista dagli script CGI (Common Gateway Interface). Questi ultimi prevedono, infatti, l’allocazione di un processo CGI per ciascuna richiesta pervenuta al CGI Server. Ovviamente, i thread condividono le risorse del processo associato alla Servlet e quindi si può verificare facilmente un’esecuzione concorrente dei metodi di quest’ultima. Figura 11 - Processi CGI Figura 12 - Threads Servlet 18 Capitolo I – Il Pattern MVC _________________________________________________________________ Figura 13 - Esempio di threads Servlet La fase di distruzione viene eseguita quando c’è lo shutdown del Web Container oppure è richiesta esplicitamente quando avviene il ricaricamento o la rimozione dell’applicazione. Il contenitore non fa altro che invocare il metodo destroy(), il quale può essere soggetto ad overriding qualora si rendesse necessario liberare delle risorse che sono state occupate attraverso il metodo init(). L’esecuzione corretta della procedura di distruzione non è sempre garantita, ad esempio nel caso in cui si verifichi un crash dell’applicazione oppure del Web Container. L’esecuzione delle Servlet in ambiente multithread comporta il notevole vantaggio dell’ottimizzazione delle risorse, ma porta con se la necessità di fare molta attenzione da parte dello sviluppatore, dovendo gestire la sincronizzazione e l’esecuzione concorrente dei thread. 5.3 Richiesta, Risposta, Sessione e Contesto Nel momento in cui una Servlet viene invocata, le vengono passati gli oggetti che contengono le informazioni relative alla richiesta pervenuta ed alla risposta che sarà generata. Tali oggetti sono accessibili all’interno dei metodi della Servlet, così come gli oggetti che conterranno le informazioni relative alla sessione utente ed all’applicazione. 19 Capitolo I – Il Pattern MVC _________________________________________________________________ La classe HTTPServletRequest contiene tutte le informazioni che riguardano la request che è giunta alla Servlet. Essa ha due funzione principali : - contiene una mappa dei parametri forniti attraverso la “query string”; - contiene le informazioni tipiche di una richiesta HTTP (es. metodo GET/POST, URI, headers,…); La classe HTTPServletResponse è necessaria per generare la risposta da inviare al client, per cui ha le seguenti funzioni principali : - consente di specificare le intestazioni HTTP del messaggio di risposta; - consente di produrre il corpo del messaggio di risposta stampandone il contenuto; L’ordine delle operazioni da eseguire è fondamentale, in quanto deve rispecchiare la struttura di una risposta HTTP che prevede in primo luogo le intestazioni (headers) e successivamente il corpo (body). La classe HTTPSession rappresenta una sessione di lavoro tra il client ed il server che viene gestita in maniera trasparente dal contenitore. Quest’ultimo mantiene una tabella contenente i dati delle sessioni instaurate con i client e consente di manipolarli utilizzando appunto l’oggetto session. Tale oggetto viene restituito invocando il metodo getSession() della classe HTTPServletRequest, in quanto le informazioni di sessione sono strettamente legate alla richiesta. Ogni sessione prevede che il client accetti i cookie, così come prevede un timeout fissato dal contenitore, allo scadere del quale la sessione viene automaticamente chiusa. Un’istanza della classe HTTPSession è notevolmente utile per poter mantenere in memoria una mappa di variabili, associate ad un unico client, per tutta la durata della sessione. Tali variabili non sono assolutamente visibili da parte di altri client. La classe ServletContext rappresenta il “contesto” dell’applicazione Web in cui la Servlet viene eseguita. Essa risulta utile per garantire la comunicazione tra più Servlet facenti parte della stessa applicazione oppure per poter mantenere in memoria una serie di variabili che saranno condivise tra più client. 20 Capitolo I – Il Pattern MVC _________________________________________________________________ Per quanto riguarda la comunicazione tra più Servlet, capita frequentemente che una Servlet debba inoltrare una richiesta ad un’altra Servlet. La problematica principale è che la Servlet chiamante non dispone di un riferimento alla Servlet chiamata e quindi non ne può invocare i suoi metodi. Le soluzioni possibili sono le seguenti : - redirezione della risposta; - inoltro all’interno del contenitore; La prima soluzione corrisponde alla produzione di una risposta di tipo 3xx per indicare al browser che è necessario richiedere un URI diverso con lo svantaggio di innescare una nuova richiesta HTTP. La seconda soluzione prevede che una Servlet richieda al contenitore l’inoltro delle richiesta, in modo che tutto resti confinato nell’ambito di un’unica transazione HTTP. Per eseguire questo compito la Servlet può fare uso di un’istanza della classe RequestDispatcher che è possibile ricavare dal ServletContext. 6. Web Application Frameworks Il Model 2 può essere considerato la base perfetta sulla quale fondare la realizzazione delle Web application. Infatti, nello sviluppo di applicazione software per il Web, molti sviluppatori hanno evidenziato come alcune parti di applicazioni diverse basate sul Model 2, siano notevolmente simili. Un caso tipico è il Controller realizzato attraverso una o più Servlet. Tali parti possono essere realizzate in modo tale da poter essere riutilizzate più volte, concetto che è alla base di un “design pattern”. Partendo, quindi, da un insieme di elementi precostituiti si ha a disposizione un “framework”. Per definizione, un framework è caratterizzato da un insieme di classi e di interfacce che possono essere usate ed eventualmente estese dagli sviluppatori e che rappresentano le infrastrutture sulla base della quale realizzare una Web application. 21 Capitolo I – Il Pattern MVC _________________________________________________________________ Le motivazioni principali che spingono verso l’utilizzo di un framework sono le seguenti : - è possibilie disaccoppiare il Presentation layer e la business-logic in componenti distinti; - si dispone di un punto centrale di controllo; - è disponibile un set molto ricco di funzionalità; - è facilitato il testing delle singole unità del sistema e la sua manutenzione; - c’è il supporto da parte di numerosi ambienti di sviluppo; - c’è la garanzia di una buona stabilità; - è garantita l’nternazionalizzazione dei contenuti; - è semplificata la validazione dei dati di input; 7. Struttura di una Web application in Java Per definizione, una Web application è una collezione di componenti separati ma che possono comunicare fra loro, costituendo un software che possa essere installato ed eseguito in un Web Container anche in maniera concorrente. Ciò vuol dire che più istanze della stessa applicazione possono essere eseguite nel Web Container, ma è ovviamente necessario poter distinguere un’istanza dall’altra mediante URL e nomi differenti. In generale, un’applicazione di questo tipo è costituita dai seguenti componenti : - Servlets; - Pagine JSP; - JavaBeans; - Pagine HTML; - File multimediali, quali (immagini, suoni, video, …); - Applets, fogli di stile (CSS) e file JavaScript; Tipicamente, questi elementi sono disposti all’interno dell’applicazione secondo una struttura gerarchica suddivisa in directory. 22 Capitolo I – Il Pattern MVC _________________________________________________________________ Partendo dalla radice (root) dell’applicazione, una directory di fondamentale importanza è la directory WEB-INF, nella quale vengono memorizzate le metainformazioni della Web application ed, in particolare, il deployment descriptor. Le risorse in essa contenute non sono accessibili dal client, ma solo ed esclusivamente dalle Servlet e dalle classi Java. All’interno di questa directory, ci sono le seguenti due directory : - WEB-INF/classes : contiene i package e le relative classi Java che sono utilizzate all’interno dell’applicazione; - WEB-INF/lib : contiene i file JAR (Java Archive), all’interno dei quali ci sono ulteriori classi distribuite nell’applicazione; Al di là di queste directory che costituiscono la base della Web application, lo sviluppatore può poi organizzare tutte le risorse di quest’ultima in ulteriori sottodirectory della root, sulla base delle funzionalità e del loro tipo. Inoltre, la piattaforma Java permette di distribuire un’applicazione Web mediante la realizzazione di un file WAR (Web Archive) , nel quale sono incluse tutte le risorse di quest’ultima. Tale file può essere facilmente distribuito su un qualsiasi Web Container che si occupa, in maniera automatica, della sua scompattazione restituendo la struttura originaria delle directory . 7.1 Web Application Deployment Descriptor Il Web Application Deployment Descriptor è un file XML, tipicamente chiamato web.xml, all’interno del quale ci sono tutte le informazioni di configurazione dell’applicazione, che vengono utilizzate dal Web Container in fase di start-up della stessa. Nell’ambito dell’utilizzo di un framework, le informazioni principali che costituiscono tale file sono le seguenti : - definizione della Servlet che funge da Controller e mapping delle richieste su di essa; 23 Capitolo I – Il Pattern MVC _________________________________________________________________ - dichiarazione dei parametri iniziali che sono passati alla Servlet in fase di start-up; - configurazione delle librerie di tag (Tag Libraries) da adottare; - elenco dei file di benvenuto dell’applicazione (Welcome File List); - utilizzo di eventuali filtri per il filtraggio delle richieste prima che vengano passate alla Servlet Controller; Per la definizione della Servlet che funge da Controller dell’applicazione, viene utilizzato il tag <servlet>, mediante il quale vengono specificate le seguenti informazioni : - il nome da assegnare alla Servlet, mediante il quale fare riferimento ad essa nel resto del file (<servlet-name>); - la classe che definisce la Servlet (<servlet-class>); Il passo successivo è quello di fare in modo che, ciascuna richiesta di un certo tipo, ad esempio verso risorse con una particolare estensione, vengano smistate dal Web Container verso la Servlet specificata. Tale operazione è nota come “mapping”, in quanto viene eseguita una vera e propria mappatura fra le tipologie di richieste e la Servlet. Per questo scopo, viene utilizzato il tag <servlet-mapping> all’interno del quale sono specificate le seguenti informazioni : - il nome della Servlet a cui inoltrare le richieste (<servlet-name>); - elenco delle estensioni che devono avere le richieste per essere inoltrate alla Servlet suddetta (<url-pattern>); Considerando l’utilizzo di un framework come Struts e JavaServer Faces, la Servlet che funge da Controller ha un parametro iniziale che è rappresentato dal file di configurazione XML dell’applicazione. In questo modo, nella fase di start-up di quest’ultima, la Servlet del framework esegue l’operazione di parsing e caricamento del contenuto del file, all’interno della memoria. Per questo scopo, viene adottato il tag <init-param>, mediante il quale è possibile specificare un qualsiasi tipo di 24 Capitolo I – Il Pattern MVC _________________________________________________________________ parametro iniziale della Servlet, oltre all’applicazione particolare suddetta. All’interno del tag, possono essere definite le seguenti informazioni : - nome del parametro (<param-name>); - valore del parametro (<param-value>); Generalmente, una Web application prevede l’utilizzo dei normali tag HTML all’interno delle proprie pagine ma anche eventualmente ulteriori tag appartenenti a librerie particolari, come quelle che vengono fornite con i framework Struts e JavaServer Faces. Per poter rendere disponibili tali librerie, esse vanno specificate all’interno del file web.xml mediante l’utilizzo del tag <taglib>, specificando le seguenti informazioni : - la locazione fisica del file TLD (Tag Library Descriptor) che contiene la descrizione dei tag della libreria (<taglib-location>); - un URI associato alla libreria, per includerla all’interno delle pagine ed evitare di fare riferimento alla locazione fisica (<taglib-uri>); Infine, è possibile specificare un elenco di risorse, tipicamente pagine, che il Web Container invia per una certa richiesta, se quest’ultima non è completa, ossia prevede soltanto una parte dell’URL. Ciascun file viene definito con il tag <welcome-file>, all’interno del blocco <welcome-file-list>. 25 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Capitolo II – Il framework JavaSer ver Faces 1 . Introduzione JavaServer Faces (JSF) è un framework per la realizzazione di Web application secondo il pattern MVC (Model – View – Controller). In particolare, esso mette a disposizione una serie di componenti server-side per la realizzazione della UI (User Interface) dell’applicazione stessa. L’elemento principale, che caratterizza la tecnologia JSF, è l’ampio insieme delle classi e delle relative API (Application Program Interface) che permettono di : - definire i componenti dell’interfaccia utente (UI) e gestire il mantenimento del relativo stato; - gestire gli eventi che si verificano sui suddetti componenti, eseguirne la conversione dei valori e la validazione degli stessi; - specificare la navigazione fra le pagine all’interno della Web application; - supportare l’internazionalizzazione e l’accessibilità; - realizzare dei componenti personalizzati (Custom Components) che estendono e potenziano le funzionalità delle classi base del framework; Inoltre, JSF mette a disposizione due librerie di tag (Tag Libraries) : - libreria HTML : per l’inserimento dei componenti della UI all’interno di una pagina JSP, secondo il linguaggio HTML; - libreria Core : per gestire il legame tra gli elementi dell’interfaccia utente e gli oggetti di back-end dell’applicazione; Inoltre, l’architettura complessiva si basa sull’utilizzo delle Servlet, nelle versioni 2.3 e 2.4, così come sulle JavaServer Pages nella versione 1.2. 26 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Figura 1 - Architettura JSF Attraverso l’utilizzo di JSF, con il minimo sforzo, è possibile ottenere i seguenti vantaggi : - gestire gli eventi che vengono generati dal lato client (client side), con oggetti e codice lato server (server side); - legare i componenti della UI con dati lato server; - costruire un’interfaccia utente con componenti riutilizzabili ed estendibili; - salvare e recuperare lo stato di ciascun componente della UI, attraverso il particolare ciclo di vita di una richiesta JSF (Faces request); Figura 2 - HTTP Request/Response Un’applicazione realizzata con JSF può essere ricondotta ad una qualsiasi Web application realizzata con il linguaggio Java. Essa si basa fondamentalmente sulle pagine JSP e sulle Servlets; basta considerare che l’applicazione stessa è gestita complessivamente attraverso una Servlet che è in esecuzione in un Servlet Container. 27 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Come si può osservare dalla figura, l’interfaccia utente che viene realizzata con JSF è in esecuzione sul server e viene visualizzata nella risposta al client. Figura 3 - Comunicazione Client/Server Per realizzare ciascuna pagina JSP e quindi la UI, è possibile sfruttare i tag che la tecnologia JSF mette a disposizione attraverso le sue librerie. Mediante tali tag, direttamente dall’interfaccia utente, si referenziano tutti gli oggetti che compongono la Web application, in particolare : - gli oggetti che rappresentano i componenti della UI mappati attraverso una serie di tag, direttamente sulle pagine JSP; - gli oggetti event listeners, validators e converters, rispettivamente per la gestione degli eventi, la validazione e la conversione associati ai componenti della UI; - gli oggetti che incapsulano le funzionalità specifiche dell’applicazione, tipicamente realizzati attraverso dei JavaBeans; Inoltre, l’applicazione può fare uso di una serie di classi di “helper”, realizzate dallo sviluppatore per accedere al back-end del sistema ed eventualmente ad una base di dati. Infine, viene definito un file di configurazione dell’applicazione nel linguaggio XML, facilmente gestibile e modificabile, per parametrizzare tutti gli aspetti di funzionamento principali dell’applicazione stessa e le relative risorse. Uno dei vantaggi principali che offre la tecnologia JavaServer Faces è la separazione fra il comportamento (Application processing layer – Model) e la presentazione 28 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ (Presentation layer - View) di una Web application, per favorire il team-working (lavoro di gruppo) separando i compiti degli sviluppatori software e dei designers. Un ulteriore vantaggio è caratterizzato dal fatto che la presentazione non prevede l’utilizzo di una particolare tecnologia o di un particolare linguaggio di markup, anche se in generale si può preferire far uso delle pagine JSP, considerando le librerie che JSF mette a disposizione. E’ ovvio che, nel caso in cui si dovesse utilizzare un particolare dispositivo client per accedere all’applicazione stessa, ad esempio un cellulare in luogo di un personal computer, si può prevedere un’interfaccia utente realizzata con una tecnologia ed un linguaggio di markup differenti (WML in luogo dell’HTML). Figura 4 - Accesso con dispositivi Client diversi 2. Teamworking Grazie alla divisione strutturale che comporta JSF in una Web application, lo sviluppo e la manutenzione di quest’ultima possono procedere rapidamente e nel modo più semplice possibile. In moltissimi team di sviluppo il singolo sviluppatore, talvolta, si occupa di numerosi e differenti compiti che in alcuni casi non sono di sua stretta competenza. Mediante la tecnologia JavaServer Faces è possibile evitare ciò, assegnando ad ogni componente del team di sviluppo una sua responsabilità ben precisa. 29 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Tipicamente, i membri assumono i seguenti ruoli : - Page author : colui che usa un linguaggio di markup, come l’HTML, ed ha esperienza di sviluppo grafico per la realizzazione delle pagine. Con l’utilizzo di JSF, egli usa anche i tag delle librerie standard o delle librerie custom, realizzate dagli sviluppatori del software; - Application developer : sviluppa gli oggetti, i gestori degli eventi (event handlers o event listeners), i validatori (validators) ed i convertitori (converters) ed in alcuni casi anche le “helper” class per l’accesso alle basi di dati; - Component writer : ha esperienza con la progettazione dell’interfaccia utente delle applicazioni e preferisce realizzare dei Custom Components per la UI, estendendo le funzionalità degli Standard Components messi a disposizione da JSF; - Application architect : colui che definisce l’architettura dell’applicazione, assicurandone la scalabilità, definendo la navigazione fra le pagine, configurando i JavaBeans e registrando gli oggetti; - Tools vendors : le case produttrici che realizzano gli ambienti di sviluppo che supportano la tecnologia JSF, per poter realizzare l’interfaccia utente in maniera semplice, attraverso il “Drag & Drop” dei componenti; Figura 5 - Teamworking 30 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Infine, sviluppare una Web application con JSF, generalmente prevede i seguenti passi fondamentali : - Creare le pagine JSP utilizzando i componenti della UI ed i tags delle librerie a disposizione; - Definire la navigazione fra le pagine dell’applicazione stessa, modificando opportunamente il file di configurazione dell’applicazione (faces-config.xml); - Sviluppare i backing beans che implementano le funzionalità offerte; - Aggiungere le informazioni riguardanti i backing beans all’interno del file di configurazione dell’applicazione; Questi compiti possono essere eseguiti rigorosamente in ordine oppure in parallelo, garantendo comunque la possibilità di comunicazione fra le persone del team di sviluppo. Ad esempio, colui che sviluppa le pagine deve comunque conoscere i nomi degli oggetti (backing beans) che realizzano le funzionalità della Web application, per richiamarli dalle pagine stesse, comunque disinteressandosi della loro implementazione interna. 3. Modelli architetturali - Framework Models Il framework JSF è costituito da un insieme di classi che sono raggruppate in modelli (models), in relazione a specifici aspetti realizzativi di una Web application. In particolare : - Execution Model; - User Interface Component Model; - Component Rendering Model; - Conversion Model; - Event and Listener Model; - Validation Model; - Navigation Model; 31 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Ad essi va aggiunto il Backing Bean Management, il quale permette la realizzazione dei backing beans (menaged beans) che implementano la business-logic dell’applicazione. Di seguito, verranno descritti tali modelli mettendo in risalto quelle che sono le caratteristiche principali delle classi che entrano in gioco per ciascuno di essi ed , inoltre, esplicitando la loro interazione che determina il funzionamento del framework. 3.1 Execution Model Questo modello fa riferimento a tutto ciò che riguarda l’esecuzione dell’applicazione, quindi in particolar modo definisce le classi che costituiscono il Controller del pattern MVC. 3.1.1 FacesServlet La classe principale su cui si basa il funzionamento del framework è la classe FacesServlet. Quest’ultima rappresenta la vera e propria Servlet in esecuzione nel Web Container, che riceve le richieste da parte dei client occupandosi della loro gestione ed assumendo quindi il ruolo di Controller secondo il modello MVC. Come tutte le Servlet, secondo l’implementazione in Java, essa prevede i metodi di inizializzazione (init()), servizio di una richiesta (service()) e di distruzione (destroy()), mediante i quali viene avviata dal Web Container all’arrivo della prima richiesta, serve quest’ultima e tutte le richieste successive e viene infine distrutta, tipicamente nei casi in cui il Web Container venga interrotto o riavviato. La FacesServlet va configurata nel Deployment Descriptor web.xml tipico di ogni Web Application realizzata in Java : <servlet> <servlet-name>FacesServlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> 32 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Viene specificata la classe e quindi un nome da assegnare alla Servlet, nonché un ordine di sequenza di start-up, nel caso in cui la Web Application preveda l’utilizzo di più Servlet per poter gestire richieste di tipo diverso. Ovviamente la FacesServlet ha il compito di gestire le richieste per file del tipo .jsf oppure .faces e per questo motivo è necessario specificarne il mapping su di esse : <servlet-mapping> <servlet-name>FacesServlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> Con questa configurazione, ogni qualvolta giunge una richiesta di una pagina .jsf al Web Container, quest’ultimo passa tale richiesta alla FacesServlet che si occuperà di gestirla. 3.1.2 Lifecycle - PhaseListener Ogni richiesta di tipo JSF (Faces Request), che arriva alla Web application, ha un proprio ciclo di vita (lifecycle) che prevede l’esecuzione di una serie di fasi, fino alla determinazione della risposta. Di tali operazioni, si occupa la classe Lifecycle che riceve ciascuna richiesta dalla FacesServlet ed esegue le varie fasi, mediante il proprio metodo execute(). Inoltre, ad essa è associata l’interfaccia PhaseListener, la quale permette di notificare l’inizio e la fine di ciascuna fase. Tale interfaccia rappresenta un punto di estensione del framework, in quanto è possibile definire alcune elaborazioni specifiche da eseguire prima o dopo una certa fase. Per fare ciò, è necessario realizzare una classe che implementi l’interfaccia PhaseListener ed esegua l’overriding dei metodi beforePhase() ed afterPhase(), all’interno dei quali sarà contenuto il codice da eseguire. Tale classe va poi associata ad una specifica fase e legata all’istanza di Lifecycle, in modo che, prima o dopo questa fase, saranno eseguite le elaborazione richieste. 33 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 3.1.3 Application La classe Application è di tipo “singleton” ed il suo scopo è quello di descrivere interamente la Web application ed in particolare i parametri di quest’ultima che sono specificati nel file di configurazione faces-config.xml. In pratica, all’avvio dell’applicazione, viene eseguita la lettura mediante un parsing XML, del file di configurazione e tutte le informazioni in esso contenute vengono caricate all’interno delle proprietà corrispondenti della classe Application. In questo modo, durante l’esecuzione dell’applicazione è possibile accedere a queste informazioni ed eventualmente modificarle. Le classi principali che sono legate ad essa e che vengono specificate nel file facesconfig.xml sono le seguenti : - ActionListener : per la gestione degli action events; - NavigationHandler : il cui scopo è quello di gestire la navigazione fra le pagine sulla base delle navigation-rules e degli outcome; - ViewHandler : ha il compito di gestire le fasi di ricostruzione della vista di una pagina da uno stato precedente e di visualizzare una risposta, anche sulla base di dispositivi client di accesso differenti; - StateManager : per il salvataggio ed il ripristino dello stato dell’applicazione fra una richiesta ed un’altra; - MessageBundle : che identifica il file .properties nel quale ci sono tutti i messaggi da visualizzare nell’applicazione, sulla base di un certo Locale, permettendo l’internazionalizzazione; - Lifecycle, PhaseListener : per la gestione del ciclo di vita di ciascuna richiesta; Se tali classi non vengono specificate nel file faces-config.xml, verranno utilizzate quelle di default. In caso contrario, lo sviluppatore ha la possibilità di personalizzare il framework, estendendo queste classi e specificando le proprie classi così ottenute nel file di configurazione. In questo modo, ad esempio, è possibile realizzare un proprio NavigationHandler che esegua delle operazioni particolari prima di far proseguire la navigazione. Lo stesso tipo di operazione può essere eseguita per le altre classi. 34 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Altre informazioni associate alla classe Application e quindi contenute nel file facesconfig.xml, specificano il Renderer Kit di default per la visualizzazione delle pagine e l’internazionalizzazione, definendo il Locale di default e tutti quelli supportati. Infine, essa è legata a tutte quelle classi che definiscono gli elementi costitutivi dell’applicazione, in particolare : - UIComponent : che definisce ciascun componente dell’interfaccia utente insieme a tutte le sue classi derivate; - Validator : che implementa un validatore per i dati di input dell’utente; - Converter : che permette di eseguire la conversione dei dati; - MethodBinding, ValueBinding : che associano i componenti ed i loro valori, ai metodi o alle proprietà dei backing beans; 3.1.4 FacesContext - ExternalContext La classe FacesContext definisce il “contesto” dell’applicazione, nel senso che contiene tutte le informazioni che caratterizzano una richiesta e ciò che riguarda la relativa risposta da generare. Essa viene potenzialmente modificata in ciascuna fase del ciclo di vita di una richiesta, per garantire che il contesto sia sempre aggiornato. E’ ovviamente legata alla classe Application, in quanto è possibile ricavare l’istanza di quest’ultima direttamente dalla classe FacesContext, facendo uso del metodo getApplication(). Oltre alle informazioni che riguardano l’applicazione, essa è legata anche a tutte quelle classi che si occupano in particolare della fase di “rendering” e quindi di produzione della risposta per una specifica richiesta. Nella fattispecie tali classi sono : - UIViewRoot : rappresenta il nodo radice della view che definisce l’albero dei componenti di una certa pagina; - RenderKit : è una collezione di istanze della classe Renderer che si occupano di visualizzare i componenti dell’interfaccia utente, secondo una certa grafica, in relazione al dispositivo client che viene utilizzato per accedere all’applcazione, il linguaggio di markup ed il Locale corrente; 35 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ - ResponseWriter : definisce uno stream attraverso il quale è possibile produrre, direttamente in una pagina di risposta, degli elementi ed i corrispondenti attributi come per i linguaggi HTML ed XML; - FacesMessage : classe che contiene un singolo messaggio associato ad un componente specifico e che va visualizzato in una pagina. Il messaggio in questione può essere tipicamente un messaggio di errore, ad esempio dovuto ad una validazione o conversione errata, ma anche di un qualsiasi altro tipo; Infine, attraverso il metodo getExternalContext(), è possibile ricavare l’istanza della classe ExternalContext, la quale definisce il “contesto esterno”, ossia tutto ciò che riguarda l’ambiente in cui viene eseguita l’applicazione. Da questa classe è possibile accedere direttamente a tutte le variabili il cui ambito di visibilità (scope) sia quello di tipo session, request ed application e quindi ai parametri ed agli attributi di una richiesta. Figura 6 - Class Diagram Execution Model 36 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 3.2 User Interface Component Model La tecnologia JSF mette a disposizione una serie di classi relative ai componenti della UI e le corrispondenti interfacce che ne descrivono il comportamento (behavioral interfaces) e le funzionalità, oltre al mantenimento dello stato, i riferimenti agli oggetti (backing beans) e la gestione degli eventi. Tali classi possono essere estese, permettendo allo sviluppatore di potenziare i componenti standard, aggiungendovi nuove funzionalità. La classe di base da cui derivano tutte le classi per i componenti standard è UIComponentBase, la quale definisce lo stato ed il comportamento di default di un generico componente ed a sua volta estende la classe UIComponent. Di seguito sono riportate tutte le classi che descrivono i componenti previsti in JSF : - UIColumn : generica colonna del componente UIData; - UICommand : controllo che può intercettare un action event (es. click del mouse) quando viene attivato; - UIData : permette di gestire un “binding” con una collection di dati per la loro visualizzazione. Tale collection può essere eventualmente definita con un DataModel; - UIForm : permette di raggruppare più controlli di una pagina e definire, appunto, un form di invio delle informazioni contenute. E’ paragonabile al tag “form” dell’ HTML; - UIGraphic : immagine da visualizzare in una pagina; - UIInput : permette di acquisire un’informazione di input dall’utente; - UIMessage : permette la visualizzazione di un messaggio anche sulla base della localizzazione geografica dell’utente (internazionalizzazione); - UIMessages : come il precedente, ma permette di visualizzare un gruppo di più messaggi; - UIOutput : visualizza un’informazione di output in una pagina; - UIPanel : permette di raggruppare più componenti secondo un certo layout; - UIParameter : usato insieme ad altri componenti per specificare una serie di parametri da passare ad essi; 37 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ - UISelectBoolean : controllo che permette all’utente di selezionare un valore booleano; - UISelectItem : definisce una singola item di una collection di items; - UISelectItems : definisce un insieme di items; - UISelectMany : permette all’utente di selezionare una o più items da una collection di items; - UISelectOne : permette all’utente di selezionare una sola item nell’ambito di una collection di items a disposizione; - UIViewRoot : rappresenta il nodo radice dell’albero dei componenti che, secondo una gerarchia, costituiscono una pagina; Le classi suddette, oltre ad estendere la classe base UIComponentBase, implementano anche una serie di interfacce che ne definiscono il comportamento : - ActionSource : indica che un componente può intercettare un action–event; - ValueHolder : indica che un componente può immagazzinare un local-value così come ha la possibilità di accedere al dato corrispondente all’interno del Model; - EditableValueHolder : estende ValueHolder aggiungendo ulteriori funzionalità ai componenti “editabili”, tra cui la validazione e l’intercettazione di eventi del tipo value-change; - NamingContainer : fa in modo che ciascun componente abbia un identificativo univoco; - StateHolder : segnala che un componente ha uno stato che va salvato ad ogni richiesta; In generale, la classe UICommand implementa ActionSource e StateHolder. La classe UIOutput e tutte le classi che la estendono, implementano StateHolder e ValueHolder. La classe UIInput e tutte le classi che la estendono, implementano StateHolder, ValueHolder ed EditableValueHolder. Per la realizzazione di un componente personalizzato (Custom Component) , è necessario estendere la classe UIComponentBase oppure una delle sue classi derivate in modo da sfruttare le potenzialità di queste ultime ed aggiungerne delle altre. 38 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Infine, per poter introdurre un componente all’interno di una pagina, è necessario associare ad esso un tag realizzato mediante una classe nota come Tag Handler. Tale classe è , in generale, un’estensione della classe UIComponentTag e definisce gli attributi del tag stesso. Figura 7 - Class Diagram UI Component Model / Rendering Model 39 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 3.3 Component Rendering Model L’architettura dei componenti, messi a disposizione dalla tecnologia JSF, prevede che le funzionalità degli stessi, così come lo stato ed il comportamento, vengano definite con le classi e le interfacce precedentemente viste, mentre la visualizzazione, il cosiddetto “rendering”, venga realizzata mediante altre classi specifiche note come renderer. Questa separazione comporta i seguenti vantaggi : - coloro che scrivono i componenti (Component writers) possono definire una sola volta il comportamento di un componente, mediate un’unica classe, ma possono permetterne diverse visualizzazioni, mediante molteplici renderers, in relazione ai tanti dispositivi client che possono accedere alla Web application; - coloro che realizzano le pagine (Page authors) e coloro che sviluppano l’applicazione (Application developers) possono scegliere la grafica di un certo componente, utilizzando un tag che individui la giusta combinazione fra la classe del componente stesso ed il renderer appropriato; Un Renderer Kit definisce una mappatura fra le classi dei componenti ed i tag che permettono di inserire questi ultimi nella UI, anche in base al dispositivo client di accesso alla Web application. Le varie implementazioni di JSF, forniscono un Renderer Kit standard per i client HTML, il quale permette di inserire i componenti della UI all’interno di una pagina JSP attraverso opportuni tag. 3.3.1 Renderer Questa classe permette di definire un renderer associato ad un componente. Essa converte la rappresentazione interna di un oggetto UIComponent in una rappresentazione grafica, attraverso uno stream di output sulla pagina. Mediante la realizzazione di una o più classi che estendono la classe Renderer, si possono realizzare diversi renderers che cambiano la modalità di visualizzazione dello stesso componente in base alle necessità o al dispositivo client di accesso. 40 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Ad esempio, un componente della classe UISelectOne ha tre renderers associati : il primo che visualizza il componete come un insieme di radio buttons, il secondo come una combo box e l’ultimo come una list box. Il comportamento del componente è sempre il medesimo, ossia la definizione di una lista di items e la possibilità all’utente di selezionarne una di esse, ma le modalità di visualizzazione sono molteplici. In definitiva, ogni tag della libreria HTML che JSF mette a disposizione, è associato ad una classe che definisce le funzionalità del componente (UIComponent) e ad una classe che ne permette la visualizzazione (Renderer). Un esempio tipico è la classe UICommand la cui funzionalità principale è quella di intercettare un action event (es. click del mouse) e permettere un invio di informazioni, così come il passaggio da una pagina all’altra. Questo scopo può essere raggiunto attraverso un “link” oppure un “bottone”. Questi ultimi hanno due tag associati per la loro visualizzazione e quindi due classi Renderer diverse, che ne permettono una grafica differente, pur basandosi sulla medesima classe componente (appunto UICommand). h:commandLink UICommand h:commandButton Figura 8 - Rendering UICommand Inoltre, come già anticipato, un insieme di classi Renderer costituiscono un Renderer Kit associato ad uno specifico dispositivo di accesso alla Web application, il quale si basa su un determinato linguaggio di markup. Infine, per quanto concerne la produzione degli elementi da visualizzare nella pagina, viene utilizzata la classe ResponseWriter che rappresenta uno stream di scrittura verso la pagina stessa. 41 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 3.4 Conversion Model In una Web application realizzata con JSF, generalmente ogni componente è associato ad un oggetto server side, tipicamente realizzato mediante un JavaBean, che in particolare viene definito backing bean o managed bean. Nell’ambito dell’applicazione, si accede ai valori memorizzati dai componenti, utilizzando i metodi getter e setter delle proprietà corrispondenti dei backing beans associati. Ciò vuol dire che , nel momento in cui un componente è legato ad un backing bean, esistono due viste del dato relativo : - model view (model value) : il valore è rappresentato secondo un certo tipo di dato all’interno della proprietà del backing bean (es. tipo int, long, ..) ; - presentation view (local value) : il valore è rappresentato secondo una modalità per cui può essere letto e modificato dall’utente. Ad esempio, un componente potrebbe prevedere l’immissione di una data da parte dell’utente. Il valore del componente è associato alla proprietà del backing bean legato al componente stesso. La data sarebbe rappresentata come un oggetto java.util.Date all’interno del backing bean (model view) ma attraverso una stringa del tipo “yyyy-mm-dd” nel componente (presentation view). L’implementazione JSF effettua automaticamente la conversione tra una vista e l’altra, ovviamente quando la proprietà del backing bean associato al componente è di un tipo di dato tra quelli ammessi dal componente stesso. In alcuni casi, è possibile che una proprietà di un backing bean, non sia di un tipo di dato standard tra quelli previsti dal linguaggio Java, ma sia un di un tipo di dato, magari una classe, definito dallo sviluppatore. In queste situazioni, associando il backing bean ad un componente, si rende necessaria la realizzazione di un opportuno convertitore, che ovviamente non è fornito nell’implementazione di JSF. Questa operazione è possibile sviluppando una classe che implementi l’interfaccia Converter e specificando la modalità con cui questa debba realizzare la conversione da una vista all’altro dei dati. In tal modo, si implementano dei convertitori personalizzati (Custom Converters). 42 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 3.4.1 Converter E’ l’interfaccia che deve essere implementata da una classe per poter eseguire una conversione Object-to-String (da model view a presentation view) e quella inversa String-to-Object (da presentation view a model view). Le implementazioni JSF mettono a disposizione una serie di classi standard che implementano tale interfaccia per l’esecuzione delle conversioni per i tipi di dato semplici. Ad essa è legata anche la classe ConverterException che definisce un’eccezione che può essere sollevata nel momento in cui, per un qualsiasi motivo, la conversione eseguita attraverso i metodi del converter non vada a buon fine. In tal caso, verrà creato automaticamente un oggetto FacesMessage, contenente il messaggio che segnala l’errore, che verrà memorizzato nel FacesContext e visualizzato all’utente. Inoltre, essa è caratterizzata fondamentalmente da due soli metodi getAsObject() e getAsString() che eseguono le conversioni in un verso e nell’altro. Infine, per poter associare un converter ad un componente dell’interfaccia utente, è necessario definire un tag mediante la classe ConverterTag. Figura 9 - Class Diagram Conversion Model 43 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 3.5 Event and Listener Model Il modello che definisce gli eventi (events) ed i relativi gestori (listeners o handlers) di JSF è molto simile al modello degli eventi dei JavaBeans, in quanto fortemente tipizzato e basato su una serie di classi e di interfacce che permettono di intercettare e gestire gli eventi dei componenti della UI. Un oggetto Event identifica il componente su cui si è scatenato l’evento e memorizza le informazioni dell’evento stesso. Per notificare il verificarsi di un evento, una Web application deve prevedere un oggetto della classe Listener che va registrata sul componente in questione. Nel momento in cui l’utente, interagendo con la UI, fa scatenare l’evento sul componente, entra in gioco il listener che si occupa di gestire l’evento. La tecnologia JSF supporta tre tipi di eventi : - action event : si scatena quanto l’utente attiva un componente che implementa l’interfaccia ActionSource, come nel caso di bottoni o collegamenti ipertestuali (link); - value-change event : si verifica quando l’utente cambia il valore di un componente implementato dalla classe UIInput o da una delle sue sottoclassi, come il caso dei semplici campi di testo, checkbox, radio buttons, list box, combo box e drop-down list; - data-model event : si scatena quando c’è uno spostamento ad una nuova riga in un componente della classe UIData; Ci sono due modalità per poter gestire un evento : - implementare una classe listener e registrarla sul componente di interesse utilizzando gli attributi actionListener oppure valueChangeListener del tag del componente stesso; - implementare all’interno del backing bean associato al componente, un metodo che debba gestire l’evento; 44 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 3.5.1 FacesEvent E’ la classe base che definisce il generico evento che si può verificare su un componente dell’interfaccia utente e memorizza tutte le informazioni che riguardano proprio quest’ultimo. Da essa derivano due sottoclassi : - ActionEvent : che definisce un action event; - ValueChangeEvent : che notifica il verificarsi di un value-change event; Ovviamente, tale classe è legata al generico UIComponent su cui l’evento è stato intercettato. 3.5.2 FacesListener E’ l’interfaccia mediante la quale è possibile realizzare una classe che abbia la capacità di gestire un generico FacesEvent. Da essa derivano due interfacce : - ActionListener : interfaccia capace di gestire un action event; - ValueChangeListener : interfaccia per la gestione di un value-change event; Ovviamente, esse utilizzano rispettivamente le classi ActionEvent e ValueChangeEvent, per avere informazioni sull’evento che ciascuna di esse deve gestire. Inoltre, quando lo sviluppatore ha la necessità di realizzare dei propri listeners per gestire dei particolari eventi, può realizzare delle classi che implementano le interfacce suddette. Infine, le interfacce e le classi listeners in questione sono legate alla classe AbortProcessingException, che rappresenta l’eccezione che può essere sollevata nel momento in cui si verifichi un errore durante la gestione di un evento. 45 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Figura 10 - Class Diagram Event and Listener Model 3.6 Validation Model La tecnologia JSF fornisce un particolare meccanismo per la validazione del local value di un componente “editabile” (es. campo di testo). Tale validazione viene eseguita prima che il model value corrispondente venga aggiornato ed assuma come valore proprio il local value. Come nel caso del Conversion Model, anche il Validation Model fornisce una serie di classi standard per effettuare le operazioni di validazione. Inoltre, all’interno della Core Library di JSF, ci sono dei tag che permettono di utilizzare tali validatori in una pagina JSP, associandoli ai componenti. 46 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ E’ comunque possibile realizzare dei validatori personalizzati (Custom Validators), nel seguenti modi : - realizzare una classe che implementa l’interfaccia Validator, registrarla all’interno dell’applicazione e definire un tag che permetta di utilizzare il validatore in una pagina JSP, associandolo ad un componente; - implementare all’interno del backing bean associato al componente, di cui fare la validazione del valore, un metodo che la esegua; 3.6.1 Validator E’ l’interfaccia che viene implementata da una qualsiasi classe che debba eseguire delle operazioni di validazione, attraverso l’overriding dell’unico metodo validate(). Le implementazioni JSF forniscono una serie di classi standard che possono eseguire delle semplici validazioni, in particolare : - LengthValidator : per verificare se il valore di un componente ha una lunghezza che rientra nei limiti specificati; - LongRangevalidator, DoubleRangeValidator : per verificare se il valore di tipo long o double di un componente, rientra in un range prefissato; E’ possibile, ovviamente, realizzare un validatore personalizzato (Custom Validator) implementando questa interfaccia. Inoltre, il metodo validate() che esegue la vera e propria validazione, può sollevare un’eccezione del tipo ValidatorException nel momento in cui si è verificato un problema nel processo di validazione. Infine, è ovvio che a ciascun validator è associato un tag, in modo da poterlo utilizzare in una pagina JSP in relazione ad uno specifico componente. La classe che permette di realizzare il Tag Handler corrispondente è la classe ValidatorTag. 47 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Figura 11 - Class Diagram Validation Model 3.7 Navigation Model Tipicamente, una Web application è costituita da un insieme di pagine. Una delle prime operazioni da eseguire nell’ambito dello sviluppo, è quella di definire la navigazione fra di esse. Nell’ambito della tecnologia JSF, la navigazione (navigation) tra le pagine è definita da una serie di regole (navigation-rules) che individuano la pagina successiva dopo aver cliccato su un bottone oppure su un link. Tali regole sono definite all’interno del file di configurazione dell’applicazione, mediante una serie di elementi XML. Per definire la navigazione bisogna : - specificare le regole di navigazione, ciascuna delle quali indica a partire da una certa pagina, quale sia la pagina successiva verso la quale proseguire; - definire in corrispondenza dei bottoni e dei link, il cosiddetto outcome ossia una stringa che permette di selezionare una tra le regole ammesse e quindi permettere la navigazione; 48 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Ad esempio, quando si clicca su un bottone oppure su di un link, viene generato un action event gestito dall’istanza ActionListener di default, che invoca un actionmethod, implementato all’interno di un backing bean. Tale metodo esegue eventualmente un’elaborazione e determina l’outcome che viene passato al NavigationHandler di default, il quale analizza le regole di navigazione, cerca l’outcome che gli è stato fornito ed individua la pagina successiva. Ciascuna navigation-rule specifica come eseguire la navigazione da una certa pagina verso tutta un’altra serie di pagine, attraverso delle casistiche di navigazione (navigation-cases). Ciascun navigation-case, all’interno di una navigation-rule, specifica una pagina di destinazione e l’outcome che permette di referenziarla per proseguire la navigazione. 3.8 Backing Bean Management Una funzione particolarmente critica di una Web application è la gestione delle risorse disponibili. Ciò prevede una separazione degli oggetti che definiscono i componenti della UI e gli oggetti che implementano le funzionalità disponibili. Un’applicazione JSF prevede una serie di backing beans, che sono associati ai componenti dell’interfaccia utente in una pagina JSP. Un backing bean è associato ad uno o più componenti della UI, in quanto ciascuna delle sue proprietà è legata al valore del componente stesso oppure ad una sua istanza. Inoltre, il bean può avere dei metodi che permettono di eseguire ulteriori elaborazioni sui componenti, come la validazione, la gestione degli eventi e il processo di navigazione. Il binding tra il valore di un componente della UI oppure di una sua istanza e le proprietà di un backing bean, viene realizzato con la sintassi dell’Expression Language (EL), così come è possibile esprimere il riferimento ad un metodo del backing bean dal componente stesso. Nel primo caso si parla di value-binding, mentre nel secondo di method-binding. Se si considera un componente dell’interfaccia utente, immesso in una pagina JSP mediante l’uso di un particolare tag, il suo valore sarà legato ad una proprietà di un backing bean mediante l’attributo value, referenziando tale proprietà mediante l’EL. In questo modo, il componente avrà un suo local value ed il corrispondente model value sarà memorizzato nella proprietà del bean. 49 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Nel caso in cui si voglia associare l’istanza del componente e non il suo valore, ad una proprietà di un backing bean, si può utilizzare l’attributo binding. Inoltre, è possibile utilizzare gli attributi validator, actionListener e valueChangeListener per fare riferimento ad alcuni metodi dei backing bean, per poter eseguire delle operazioni di validazione e per poter gestire gli eventi del tipo “action” e “valuechange”. Ad esempio, considerando un campo di testo il cui valore deve essere trasferito nella proprietà di un backing bean, dopo essere stato validato attraverso un metodo di quest’ultimo, la sintassi del tag <h:inputText> è la seguente : <h:inputText id="idCampoTesto" value="#{backingBean.proprieta}" validator="#{backingBean.metodoValidazione}" valueChangeListener="#{backingBean.metodoEvento}"/> Sulla base di questa definizione, se il valore del componente subisce un cambiamento ed il form che lo contiene viene trasmesso, si scatena un value-change event che verrà gestito dal metodo del backing bean specificato. Infine, nel momento in cui si ha a che fare con un componente che implementa l’interfaccia ActionSource (es. bottone o link), si può utilizzare l’attributo action per specificare il metodo di un backing bean che esegue una certa elaborazione e fornisce come risultato un outcome per proseguire la navigazione in una certa direzione. <h:commandButton id="idBottone" action="#{backingBean.metodo}"/> Eseguire binding fra l’istanza di un componente e la proprietà di un bean, ha i seguenti vantaggi : - il bean può modificare gli attributi del componente durante l’esecuzione; - il bean può istanziare un componente; Invece, eseguire il binding fra il valore del componente e la proprietà del bean, ha i seguenti vantaggi : - colui che ha realizzato la pagina ha più controllo sugli attributi dei componente; 50 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ - i backing beans non sono legati alle classi dei componenti e quindi si ha una netta separazione fra Model e View; - l’implementazione JSF può realizzare la conversione tra il valore del componente e la relativa proprietà del backing bean in maniera automatica, senza che lo sviluppatore debba realizzare un convertitore, se non in casi particolari; I backing beans sono configurati nel file faces-config.xml che viene valutato all’avvio dell’applicazione, rendendo disponibili tali beans che vengono poi istanziati quando sono referenziati all’interno dei tag. <managed-bean> <managed-bean-name>backingBean</managed-bean-name> <managed-bean-class> package.ClasseBackingBean </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> Gli ambiti di visibilità (scope) in cui sono accessibili i backing beans, sono : - request : un bean è accessibile soltanto in corrispondenza di una richiesta HTTP; - session : un bean è visibile durante un’intera sessione utente; - application : un bean è condiviso ed accessibile fra tutti gli utenti che accedono all’applicazione; Figura 12 - Dichiarazione Backing Beans 51 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Quando un backing bean viene referenziato all’interno di una pagina JSP, esso viene cercato dall’implementazione JSF in tutti i possibili ambiti di visibilità. La presenza dei backing beans introduce in JSF, l’uso dei pattern Inversion of Control (IoC), noto anche come Dependency Injection. Tale pattern prevede che, nell’ambito della realizzazione di un software, ci sia sempre una classe nota come “container” che ha il compito di istanziare gli oggetti della business-logic e gestirne la dipendenza e lo scambio di dati fra essi. Nel framework JSF, tutto ciò accade in maniera assolutamente automatica, in virtù del fatto che i backing beans vengono istanziati nell’ambito di visibilità corretto, nel momento in cui si fa riferimento ad essi attraverso il value-binding o method-binding. Lo sviluppatore non ha più l’onere di gestire l’allocazione degli oggetti nel momento in cui ne ha bisogno, perché il tutto viene gestito dal framework in maniera completamente trasparente. 4. Ciclo di vita di una pagina JavaServer Faces Il ciclo di vita (life-cycle) di una pagina JSF è molto simile a quello di una normale pagina JSP. Il client invia una richiesta HTTP per ricevere una certa pagina ed il server invia tale pagina trasformata in HTML. In realtà, considerando le potenzialità in più offerte dalla tecnologia JSF, sono previste alcune elaborazione aggiuntive. Una pagina JSF è rappresentata da un albero dei componenti che costituiscono l’interfaccia utente, chiamato view, caratterizzato da una gerarchia ben definito. Ad esempio, nella figura che segue, la pagina contenente il form di Login avrà : - il nodo root dell’albero; - il nodo relativo al form, legato al nodo root; - all’interno del nodo del form, i nodi associati ai tre seguenti componenti : due campi di testo ed un bottone; Ovviamente, questa è soltanto una parte della pagina, che potrebbe contenere ulteriori componenti. 52 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ :UIViewRoot loginForm :UIForm userName password :UIInput :UIInput loginButton :UICommand Figura 13 - Esempio struttura della view Quando il client richiede la pagina, inizia il life-cycle e l’implementazione JSF costruisce la view, tenendo conto dello stato degli oggetti relativo ad una richiesta precedente della pagina stessa (viene ricostruito l’ultimo stato della pagina). Il client interagisce con la pagina ed invia i dati; l’implementazione JSF esegue la validazione di questi ultimi e la conversione dei valori in tipi validi dal lato del server. Infine, vengono eseguite le funzionalità della Web application e proposti i risultati al client, nella forma della pagina HTML risultante. 4.1 Scenari di elaborazione di una richiesta Un’applicazione JSF supporta due tipi di richieste (request) e due tipi di (response) : - Faces response : risposta generata durante la fase di Render Response del ciclo di vita di una request; 53 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ - Non-Faces response : risposta che non è creata durante la fase di Render Response del ciclo di vita di una request; caso tipico è quello di una pagina JSP che non contiene componenti JSF; - Faces request : una richiesta che viene inviata da una Faces response generata in precedenza; - Non-Faces request : richiesta inviata ad un elemento come una Servlet o una pagina JSP e non direttamente ad un componente dell’albero dei componenti JSF di una pagina; La differenziazione delle richieste e delle risposte suddetta, da luogo a tre possibili scenari del ciclo di vita di una richiesta : Scenario 1 : Non-Faces request genera una Faces response Un esempio tipico si verifica nel momento in cui si clicca su semplice link HTML per aprire una pagina JSF. In questo caso, entra in gioco il Controller della tecnologia JSF, ossia una Servlet della classe FacesServlet che accetta la richiesta e la passa alla classe che implementa il ciclo di vita della stessa per elaborarla. Quando deve essere generata la Faces response, l’applicazione acquisisce i riferimenti agli oggetti necessari alla view, memorizza quest’ultima nel FacesContext (contesto contenente tutte le informazioni della richiesta) ed invoca il metodo renderResponse(), passando alla fase Render Response. Scenario 2 : Faces request genera una Non-Faces response Talvolta, un’applicazione JSF può avere la necessità di generare una response che non contiene componenti della tecnologia JavaServer Faces. In queste situazioni, lo sviluppatore deve saltare la fase di Render Response invocando il metodo responseComplete() della classe FacesContext. Tale chiamata può avvenire in una delle seguenti fasi : Apply Request Value, Process Validations, Update Model Values. 54 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Scenario 3 : Faces request genera una Faces response Questo è lo scenario tipico, che segue il ciclo di vita standard di una richiesta, in cui un componente JSF invia una request ad un’applicazione JSF utilizzando la FacesSarvlet. Tutti i listeners, validators e converters sono invocati nella fase opportuna del ciclo di vita stesso. 4.2 Ciclo di vita Standard Il ciclo di vita standard di una richiesta è quello dello terzo scenario, che è stato introdotto in precedenza. La tecnologia JSF permette di gestire due tipologie di richieste : richiesta iniziale (initial request) e postback. L’initial request è caratterizzata dal fatto che il client richiede per la prima volta una certa pagina, mentre un postback scaturisce dall’invio di un form che è stato generato da un’initial request precedente. Quando deve essere gestita un’initial request, vengono eseguite solo le fasi di Restore View e Render Response, perché non ci sono azioni da processare, mentre nel caso di un postback vengono eseguite nell’ordine tutte le fasi. Figura 14 - Ciclo di vita Standard 55 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 4.2.1 Restore View Una richiesta per una pagina JSF viene generata nel momento in cui si clicca su di un link oppure su di un bottone inviando le informazioni di un form; in entrambe le situazioni viene avviata la prima fase del life-cycle, la Restore View. Durante questa fase, viene costruita la view della pagina (albero degli oggetti associati ai componenti della UI), vengono associati gli events handlers (listener), i validators ed i converters ai componenti ed infine la view viene memorizzata nel FacesContext, che avrà tutte le informazioni per gestire la richiesta. Se si tratta di una initial request viene creata una view vuota e si passa direttamente alla fase di Render Response. Tale view vuota sarà popolata quando, ad esempio, l’utente immetterà dei dati in un form e verrà comandato un postback. Se si tratta di un postback, la view precedente relativa alla pagina già esiste e viene recuperata, utilizzando le informazioni di stato dei componenti della UI che sono salvate sul client oppure sul server (per default). 4.2.2 Apply Request Values Dopo aver recuperato l’albero dei componenti (view), ogni componente estrae il suo nuovo valore dai parametri della richiesta, usando il metodo decode(). Tale valore, noto come submittedValue, viene memorizzato localmente al componente stesso. Se la conversione del valore fallisce, viene memorizzato nel FacesContext un messaggio di errore che sarà visualizzato nella fase di Render Response, insieme ad eventuali altri errori di validazione successivi. Se un metodo decode() oppure un event listener invoca il metodo renderResponse() del FacesContext, si salta direttamente alla fase di Render Response. Se degli eventi vengono accodati in questa fase, ne verrà eseguito il broadcast (smistamento) verso i corrispondenti listeners, ma la loro gestione avverrà nelle fasi successive. Se, invece, alcuni componenti della pagina hanno l’attributo immediate settato a true, allora la validazione, conversione e gestione degli eventi associati vengono processati subito e non posticipati alle fasi successive. 56 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ A questo punto, se bisogna redirezionare il flusso dell’applicazione verso una risorsa che non ha componenti JSF, viene invocato il metodo responseComplete() del FacesContext. Al termine di questa fase, i componenti sono settati ai loro nuovi valori (submittedValues) ed i messaggi eventuali di errore e gli eventi sono messi in coda, pronti per essere processati nelle fasi successive. 4.2.3 Process Validation Durante questa fase, vengono eseguiti tutti i validators registrati sui componenti della view. In primo luogo, vengono valutati gli attributi fissati che specificano le regole di validazione e viene confrontato il valore memorizzato in ciascun componente (local-value) con le regole stesse. Se il valore locale di un componente non è valido, viene memorizzato un messaggio di errore nel FacesContext e si passa alla fase di Render Response, per visualizzare tutti i messaggi di questo tipo, insieme ad altri eventuali messaggi dovuti ad errori di conversione nella fase precedente. Se un metodo validate() oppure un event-listener invoca il metodo renderResponse() del FacesContext, si passa direttamente alla fase di Render Response. A questo punto, se bisogna redirezionare il flusso dell’applicazione verso una risorsa che non ha componenti JSF, viene invocato il metodo responseComplete() del FacesContext. Se degli eventi vengono accodati in questa fase, ne verrà eseguito il broadcast (smistamento) verso i corrispondenti listeners, ma la loro gestione avverrà nelle fasi successive. 4.2.4 Update Model Values Una volta che i valori locali dei componenti sono considerati validi, è possibile percorrere l’albero dei componenti stessi ed assegnare alle proprietà dei backing beans, tali valori. Ciò vuol dire rendere i submittedValue, valori effettivi del modello (model-value). Ovviamente i local-values devono essere convertiti 57 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ correttamente nei tipi di dati accettati dalle proprietà dei backing beans. Se si verificano degli errori, si passa direttamente alla fase di Render Response per visualizzarli, in maniera molto simile agli errori di validazione nelle fasi precedenti. Se un metodo updateModels() oppure un event listener chiama il metodo renderResponse() del FacesContext, si salta direttamente alla fase di Render Response. A questo punto, se bisogna redirezionare il flusso dell’applicazione verso una risorsa che non ha componenti JSF, viene invocato il metodo responseComplete() del FacesContext. Se degli eventi vengono accodati in questa fase, ne verrà eseguito il broadcast (smistamento) verso i corrispondenti listeners, ma la loro gestione avverrà nelle fasi successive. 4.2.5 Invoke Application In questa fase viene gestito ogni evento sia di tipo action che di tipo valuechange, come nei casi di invio di un form di click su di un link. Se bisogna redirezionare il flusso dell’applicazione verso una risorsa che non ha componenti JSF, viene invocato il metodo responseComplete() del FacesContext. Se la view è stata recuperata da una richiesta precedente ed su di un componente viene sollevato un evento, questo viene smistato verso il suo listener. 4.2.6 Render Response Durante questa fase, l’implementazione JSF delega il compito della visualizzazione (rendering) della risposta al JSP container, ovviamente nel caso in cui si utilizzino delle pagine JSP. Se si tratta di una initial request, i componenti della pagina vengono aggiunti alla view, mentre nel caso di un postback già sono presenti. In entrambi i casi, i componenti eseguono il proprio rendering sulla base dei tag che li rappresentano nella pagina JSP. Nel caso di un postback, se nelle fasi precedenti si è verificato qualche errore, viene visualizzata comunque la pagina e se ci sono in essa dei tag <h:message> oppure <h:messages>, vengono visualizzati i messaggi di errore. 58 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Una volta visualizzata la pagina, viene salvato lo stato della response, ossia lo stato dei componenti, sul client oppure sul server, in modo che per un’eventuale successiva request potrà essere eseguita la fase di Restore View. 5. JSF Expression Language Il framework JSF dispone di una particolare versione dell’Expression Language (EL), rispetto a quello disponibile nella librerie di tag JSTL (JavaServer Pages Standard Tag Library) ampiamente utilizzate con Struts. Tale linguaggio permette di definire delle espressioni, mediante le quali può essere realizzato il value-binding ed il method-bindig tra i componenti dell’interfaccia utente e le proprietà oppure i metodi dei backing beans. Infatti, è attraverso l’EL che è possibile associare la proprietà di un componente con la proprietà di un bean, così come un evento oppure un azione su un componente con il metodo di un bean. Nell’espressione viene specificato il nome del bean e la proprietà oppure il metodo a cui fare riferimento, mediante la tipica “dot notation”. #{backingBean.[proprieta | metodo]} Inoltre, esso fornisce una serie di operatori aritmetici e logici per definire espressioni molto più complesse, magari utilizzando come operandi le proprietà dei backing beans a cui si fa riferimento. Sono altresì disponibili i seguenti oggetti impliciti (implicit object) ai quali si può fare riferimento da una qualsiasi pagina JSP : - applicationScope : oggetto Map con varibiabili e beans che si trovano nell’ambito di visibilità di tipo application; - requestScope : oggetto Map con varibiabili e beans che si trovano nell’ambito di visibilità di tipo request; - sessionScope : oggetto Map con varibiabili e beans che si trovano nell’ambito di visibilità di tipo session; - cookie : oggetto Map contenente i cookie associati alla richiesta; - facesContext : istanza della classe FacesContext; 59 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ - header : oggetto Map contenente gli header HTTP della richiesta corrente; - param : oggetto Map che ha al suo interno i parametri della richiesta; - view : oggetto che rappresenta il nodo root della view; 6. Espandibilità del framework La tecnologia JavaServer Faces offre allo sviluppatore la possibilità di estendere completamente le classi del framework. Nella maggior parte dei casi, per quanto riguarda l’Execution Model, vengono utilizzate sempre le classi di default a meno di particolari esigenze. La potenzialità di personalizzare il framework viene sfruttata soprattutto per quanto riguarda la realizzazione di : - Custom Converter; - Event Listeners; - Custom Validator; - Custom UI Components; 6.1 Custom Converter L’implementazione JSF esegue automaticamente tutte le conversioni necessarie dei valori dei componenti della UI nelle corrispondenti proprietà dei backing beans. In alcuni casi particolari, può esserci l’esigenza di realizzare un convertitore personalizzato che sia capace di eseguire una conversione che non rientri tra quelle standard di JSF. Per fare questo, è necessario realizzare una classe che implementa l’interfaccia Converter. Tale classe dovrà inoltre eseguire l’overriding dei due metodi seguenti : - getAsObject() : riceve il valore del componente e lo converte nel tipo di dato della proprietà corrispondente del backing bean associato; - getAsString() : riceve il valore della proprietà del backing bean e lo converte in stringa per la visualizzazione nella UI; 60 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ public Object getAsObject(FacesContext context, UIComponent component, String value) { ... } public String getAsString(FacesContext context, UIComponent component, Object value) { ... } Durante la fase di Apply Request Values, quando l’utente ha immesso dei valori nei componenti (es. riempiendo un campo di testo), viene invocato il metodo decode() del componente oppure del renderer corrispondente, che si occupa di acquisire tale valore. Ovviamente, è in questo metodo che viene invocato getAsObject(). Viceversa, durante la fase di Render Response, quando bisogna visualizzare nell’interfaccia utente il valore di un componente, vengono invocati i metodi di encoding del componente stesso oppure del renderer associato, che a loro volta invocano getAsString(). Infine, per rendere disponibile il converter nell’applicazione, bisogna registrarlo nel file faces-config.xml. Inoltre, affinché lo si possa associare ad un componente, è necessario usare l’attributo converter del componente stesso oppure il tag <f:converter> della Core Library di JSF. <f:converter converterId="idConverter"/> 6.2 Event Listener Come visto in precedenza, l’implementazione JSF permette di gestire due tipologie di eventi che si possono verificare sui componenti della UI : - action event; - value-change event; 61 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Tale gestione, può essere eseguita in due modi diversi : - realizzare una classe listener che implementi l’interfaccia ActionListener oppure ValueChangeListener; - realizzare, all’interno di un certo backing bean, un metodo che sia capace di gestire l’evento; 6.2.1 Implementazione di un ActionListener/ValueChangeListener Per poter gestire un action event che può essere sollevato da un componente, è possibile sviluppare una classe che implementa l’interfaccia ActionListener. All’interno di essa, sarà eseguito l’overriding del metodo processAction(), il quale prevede come parametro di ingresso un oggetto di tipo ActionEvent che contiene tutte le informazioni sull’evento e sul componente che l’ha generato. Ovviamente, all’interno di questo metodo, sarà necessario scrivere il codice per la gestione dell’evento, in quanto il metodo sarà immediatamente invocato, nel momento in cui l’evento sarà intercettato. public void processAction(ActionEvent event) throws AbortProcessingException { ... } Una volta realizzata la classe, essa sarà associata ad un componente utilizzando il tag <f:actionListener>. <f:actionListener type="classeActionListener"/> Per poter gestire un value-change event per un certo componente, è possibile realizzare una classe che implementa l’interfaccia ValueChangeListener. Tale classe deve semplicemente eseguire l’overriding del metodo processValueChange(), il quale ha un parametro di ingresso di tipo ValueChangeEvent che contiene le informazioni sull’evento che si è verificato. Tale metodo viene invocato nel momento in cui l’evento si verifica e quindi conterrà il codice per la sua gestione. 62 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ public void processValueChange(ValueChangeEvent event) throws AbortProcessingException { ... } Infine, per poter registrare la classe listener sul componente, si può utilizzare il tag <f:valueChangeListener> della Core Library di JSF. <f:valueChangeListener type="classeValueChangeListener"/> 6.2.2 Backing Bean Method Listener Una possibile strada alternativa alla realizzazione delle classi listener, è lo sviluppo di alcuni metodi all’interno dei backing beans che abbiano la capacità di gestire gli eventi. Nel caso in cui bisogna gestire un action event, è possibile realizzare un metodo che non ha parametri di uscite ed ha come unico parametro di ingresso, un oggetto di tipo ActionEvent con le informazioni dell’evento intercettato. Tale metodo potrà essere associato al componente, utilizzando l’attributo actionListener del tag che rappresenta il componente stesso all’interno di una pagina. <h:commandLink actionListener="#{backingBean.metodoListener}"/> Se invece abbiamo a che fare con un value-change event, bisogna realizzare un metodo che ha come parametro di ingresso, un oggetto del tipo ValueChangeEvent e che sarà associato al componente, mediante l’attributo valueChangeListener del medesimo tag. <h:inputText valueChangeListener="#{backingBean.metodoListener}"/> 6.3 Custom Validator Per eseguire la validazione dei dati, JSF mette a disposizione una serie di validatori standard, ma nel caso in cui questi ultimi non siano sufficienti, è possibile realizzarne di propri. 63 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Le modalità possibili sono le seguenti : - realizzare una classe che implementa l’interfaccia Validator; - sviluppare un metodo all’interno di un backing bean, che si occupi della validazione; 6.3.1 Implementazione di un Validator Nel caso in cui la scelta sia quella di realizzare una classe che implementa l’interfaccia Validator, si deve poi poter associare tale validatore ad un qualsiasi componente che ne possa fare uso. Le possibili soluzioni sono le seguenti : - se il validator prevede degli attributi e si vuole dare la possibilità al Page author di assegnare ad essi dei valori specifici, bisogna realizzare un custom tag mediante il quale è possibile immettere il validatore all’interno della pagina. Tale tag avrà quindi degli attributi accessibili al realizzatore della pagina, secondo una modalità XML-like; - in alcuni casi si può preferire non realizzare un tag proprietario del validator. In queste situazioni, per associare il validatore ad un componente, si deve utilizzare il tag <f:validator> della Core Library , specificando la classe del validatore, ed uno o più tag <f:attribute> per specificarne gli attributi; - un ultimo caso particolare è quello in cui si realizza un Custom Component e gli si vuole associare un validatore integrato. Ciò vuol dire che quest’ultimo verrà associato al componente durante l’esecuzione, mediante il metodo addValidator(), e non ci sarà la possibilità per il Page author di farlo manualmente all’interno di una pagina. Questa soluzione “integrata” fornisce un componente potenziato di un validator integrato, ma non da flessibilità al realizzatore della pagina di poter utlizzare il validatore al di fuori del componente; Durante la validazione, si potrebbe verificare un errore ed in questo caso, sarebbe utile visualizzare un messaggio all’utente. E’ possibile creare i messaggi durante l’esecuzione mediante la classe FacesMessage, memorizzandoli nel FacesContext. In 64 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ generale, si preferisce sollevare un’eccezione del tipo ValidatorException, nel metodo validate() della classe , con il messaggio di errore da visualizzare. Nel caso più generale possibile, i passi per poter realizzare un validatore sono i seguenti : - realizzare la classe che implementa l’interfaccia Validator; - realizzare un Tag Handler; - definire un tag associato al validatore, all’interno di un file TLD (Tag Library Descriptor); 6.3.1.1 Class Validator La classe che implementa l’interfaccia Validator deve tipicamente avere una serie di proprietà, che non sono altro che gli attributi del validatore stesso. Ovviamente, deve essere anche dotata dei metodi di accesso a tali proprietà (Accessor Method), nella forma getNomeProprieta() setNomeProprieta(). Inoltre, bisogna eseguire l’overriding del metodo validate() che dovrà contenere il codice per eseguire la validazione. public void validate(FacesContext context, UIComponent toValidate, Object value) throws ValidatorException { ... } 6.3.1.2 Tag Handler Per poter associare un tag al validatore, è necessario realizzare un Tag Handler, che altro non è che una classe che estende la classe di base del framework ValidatorTag. Tale classe deve avere le stesse proprietà della classe validator che saranno gli attributi del tag. 65 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Le caratteristiche principali della classe sono le seguenti : - i metodi di accesso alle proprietà; - esegue l’overriding del metodo createValidator(), all’interno del quale viene creata un’istanza del validatore e ne vengono inizializzate le proprietà sulla base dei valori che il page author ha assegnato agli attributi del tag; protected Validator createValidator() throws JspException { ... } 6.3.1.3 Tag Library Descriptor Per poter associare il validatore ad un componente all’interno di una pagina, è necessario realizzare un tag che lo rappresenti. Fondamentalmente, il tag non fa altro che rappresentare il Tag Handler, il quale avrà poi il compito di utilizzare la classe validator. Per la descrizione dei tag, vengono utilizzati i file TLD (Tag Library Descriptor) all’interno dei quali, i tag stessi, vengono elencati con i relativi attributi, sfruttando una sintassi XML-like. <tag> <name>nomeValidator</name> <tag-class>package.ClasseValidatorTag</tag-class> <body-content>JSP</body-content> <attribute> <name>attributo</name> <required>true</required> <type>package.ClasseTipoAttributo</type> </attribute> ... </tag> 6.3.2 Backing Bean Method Validator La validazione del valore di un componente può essere eseguita anche realizzando, all’interno di un backing bean, un metodo che riceve come parametri di ingresso : l’oggetto FacesContext dell’applicazione, il componente del cui valore farne la validazione ed infine il valore stesso. Tale metodo non ha parametri di uscita ed ha 66 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ la possibilità di generare dei messaggi di errore, nel caso in cui la validazione non andasse a buon fine. public void metodoValidate(FacesContext context, UIComponent toValidate, Object value) { ... } Per fare in modo che il metodo venga eseguito, è necessario utilizzare l’attributo validator del componente il cui valore deve essere soggetto a validazione. <h:inputText id="idCampoTesto" value="#{backingBean.proprieta}" validator="#{backingBean.metodoValidate}"/> 6.4 Custom UI Components La tecnologia JSF mette a disposizione una serie di componenti standard per la realizzazione dell’interfaccia utente, ma è comunque possibile estendere le funzionalità di questi ultimi o addirittura creare di nuovi, con delle funzionalità non ancora esistenti. E’ inoltre possibile, creare uno o più renderer per poter visualizzare ciascun componente in maniera diversa su client di tipo differente. Ciò vuol dire che il comportamento del componente viene definito una sola volta, mentre la sua visualizzazione viene delegata ai renderer che possono essere molteplici. Ciò non toglie, che un componente può gestire la visualizzazione in maniera autonoma, non facendo uso dei renderer. In generale, la classe che realizza un componente, ne definisce lo stato ed il comportamento, il quale include : convertire i valori del componente attraverso i convertitori , accodare gli eventi, eseguire la validazione facendo uso dei validatori ed altre funzionalità. C’è bisogno di creare un Custom Component, in queste situazioni : - aggiungere un nuovo comportamento ad un componente standard, ad esempio un nuovo tipo di evento; - aggregare più componenti per costituirne uno solo con un unico comportamento; 67 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ - se si ha bisogno di un componente supportato da un client HTML ma non implementato da JSF; - realizzare un componente che possa essere utilizzato da un client non HTML, quando magari il componente stesso esiste per un client HTML; Non c’è la necessità di realizzare un Custom Component in queste situazioni : - se bisogna manipolare i dati di un componente o aggiungervi semplici funzionalità. In questo caso, basta creare dei metodi nel backing bean, associato ad esso; - bisogna convertire il valore di un componente in un dato non supportato dal suo renderer. In tale situazione, conviene adottare un convertitore standard o realizzarne uno personalizzato; - se bisogna eseguire la validazione sul valore del componente, conviene utilizzare un validatore standard o realizzarne uno; - se c’è la necessità di registrare degli event listeners su un componente. In tal caso, si adottano le tecniche per la realizzazione degli event listeners; Quando si crea un Custom Component, bisogna essere sicuri che la classe sia capace di eseguire le seguenti operazioni : - Deconding : prelevare, dai parametri della request, il valore del componente che è stato immesso dall’utente ed associarlo al local value; - Encoding : eseguire il rendering del componente secondo un certo linguaggio di markup, quale può essere HTML , XML , WML in relazione al tipo di client; JSF supporta due modelli di programmazione per il deconding e l’encoding : - Direct implementation : la stessa classe del componente implementa il decoding e l’encoding; - Delegated implementation : la classe del componente delega l’implementazione del decoding e dell’encoding ad un renderer; 68 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Con il secondo modello, si ha il vantaggio di poter definire il componente una sola volta (stato e comportamento) ma di avere più renderer associati ad esso, che ne permettano la visualizzazione in forme diverse a secondo del device client (browser web , cellulare,…). Nel caso di un Custom Component, si rende necessaria la realizzazione di un tag che permetta di introdurre il componente in una pagina, operazione opzionale, invece, nel caso in cui si realizzi un validator e quasi mai utilizzata nel caso di un converter. In generale, per poter realizzare un Custom Component, si seguono i seguenti passi : - sviluppare una classe che rappresenti il componente ed estenda la classe base del framework UIComponentBase oppure una delle sue derivate; - realizzare un Tag Handler; - eventualmente, sviluppare uno o più renderer estendendo la classe Renderer; - infine, descrivere il tag all’interno di un file TLD; 6.4.1. Class Component Tutti i componenti standard estendono le classi UIComponentBase oppure UIComponent. Quando si vuole creare un Custom Component, bisogna estendere una di queste ultime se lo si vuole realizzare partendo da zero. E’ possibile invece, poter sfruttare le caratteristiche di un componente standard derivando dalla sua classe. Ad esempio se bisogna creare un menù editabile, non conviene estendere la classe UIComponentBase ma la classe UISelectOne, in modo da sfruttarne il suo comportamento e doverne aggiungere solo le funzionalità necessarie. Ovviamente, il componente sarà utilizzato all’interno di una pagina ed in generale sarà associato ad un backing bean, che conterrà in una sua proprietà il valore del componente (value-binding) e potrebbe avere dei metodi referenziati dal componente stesso (method-binding). Il primo passo per la realizzazione della classe del Custom Component è quello di eseguire l’overriding del metodo getFamily() che restituisce la famiglia di appartenenza del componente, registrata anche all’interno del file faces-config.xml. 69 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ public final static String COMPONENT_TYPE ="TipoComponente"; public final static String COMPONENT_FAMILY ="FamigliaComponente"; ... ... public String getFamily() { return COMPONENT_FAMILY; } ... <component> <component-type>TipoComponente</component-type> <component-class>package.ClasseComponente</component-class> </component> Nel caso in cui il componente gestisca in maniera autonoma il rendering, deve occuparsi sia della fase di decoding che di encoding. Per quanto riguarda la fase di deconding, basta eseguire l’overriding del metodo decode() che viene invocato nella fase di Apply Values Change del ciclo di vita di una richiesta. Per quanto concerne l’encoding, bisogna eseguire l’overriding dei metodi encodeBegin(), encodeChildre() ed encodeEnd(). C’è da precisare che, nella maggior parte dei casi, basta realizzare soltanto il primo metodo; i due successivi vengono usati soltanto se il componente ha dei componenti figli al suo interno. ... public void decode(FacesContext context) { ... } ... ... public void encodeBegin(FacesContext context) throws IOException { ... ResponseWriter writer = context.getResponseWriter(); writer.startElement("nomeElemento", this); writer.writeAttribute("nomeAttributo", valore,"nomeAttributo"); ... writer.write(body); writer.endElement("nomeElemento"); ... } All’interno di ciascuno di questi metodi, si fa uso di un oggetto ResponseWriter, che permette di scrivere direttamente su una pagina, mediante uno stream di output, in un linguaggio XML-like. Si osserva che il metodo startElement() permette di scrivere sullo stream il tag di apertura, usando una o più volte writeAttribute() è possibile scrivere gli attributi ed i corrispondenti valori, con il metodo write() si scrive il body 70 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ del tag ed infine, usando endElement() si scrive il tag di chiusura. In una pagina JSP, il risultato di queste operazioni sarà una cosa del tipo seguente : <libreria:nomeElemento nomeAttributo="[valore]" /> [body] </libreria:nomeElemento> Se il rendering viene delegato ad un renderer, i metodi di decoding e di encoding non vengono realizzati, ma viene eseguito l’overriding del metodo getRendererType() che restituisce l’identificativo del renderer adottato. 6.4.2 Tag Handler La classe Tag Handler associata con un componente, estende la classe base del framework UIComponentTag e guida la fase Render Response del ciclo di vita di una richiesta. La prima operazione eseguita è quella di recuperare il tipo di componente associato al tag mediante il metodo getComponentType(). public String getComponentType() { return ClasseComponente.COMPONENT_TYPE; } Successivamente, mediante il metodo setProperties(), vengono inizializzate le proprietà del componente con i valori degli attributi del tag che sono stati specificati nella pagina JSP in cui compare il tag stesso. protected void setProperties(UIComponent component) { ... componente.setNomeAttributo(this.nomeAttributo); ... ... } Infine, tale classe, mediante il metodo getRendererType(), restituisce il tipo di renderer adottato, se ne è associato uno al componente, in modo da permettere all’implementazione JSF di eseguire il deconding e l’encoding di quest’ultimo. 71 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ public String getRendererType() { return ClasseRenderer.RENDERER_TYPE; } Ovviamente, gli attributi di un tag possono avere come valore, non una stringa, ma un’espressione nel linguaggio EL, per poter eseguire le operazioni di value-binding o method-bindig e quindi introdurre dei riferimenti alle proprietà di un backing bean oppure ai suoi metodi. Per ogni attributo che accetta una espressione EL, all’interno del metodo setPropterties(), per settare l’attributo, è necessario utilizzare le classi MethodBinding e ValueBinding. L’oggetto MethodBinding serve per valutare un’espressione relativa ad un metodo di un backing bean, mentre l’oggetto ValueBinding permette di valutare un’espressione relativa ad una delle sue proprietà. E’ raccomandabile che ogni Tag Handler abbia un metodo release() per rilasciare le risorse allocate, una volta che siano state utilizzate. 6.4.3 Tag Library Descriptor Per poter utilizzare un componente all’interno di una pagina, è necessario realizzare un tag che lo rappresenti. Fondamentalmente, il tag non fa altro che rappresentare il Tag Handler, il quale avrà poi il compito di utilizzare la classe del componente. Per la descrizione dei tag, vengono utilizzati i file TLD (Tag LIbrary Descriptor) all’interno dei quali, i tag stessi, vengono elencati con i relativi attributi, sfruttando una sintassi XML-like. <tag> <name>nomeComponente</name> <tag-class>package.ClasseComponente</tag-class> <body-content>JSP</body-content> <attribute> <name>attributo</name> <type>package.classeTipoAttributo</type> </attribute> ... </tag> 72 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ 6.4.4 Classe Renderer Nel caso in cui il componente non gestisca il decoding e l’encoding in maniera autonoma, è necessario realizzare un renderer al quale delegare queste operazioni. La classe che implementa il renderer estende la classe base Renderer e deve eseguire l’overriding dei metodi decode(), encodeBegin(), encodeChildren() ed encodeEnd() per portare a termine il suo compito. . public void decode(FacesContext context, UIComponent component) { ... } ... ... public void encodeBegin(FacesContext context, UIComponent component) throws IOException { ... } Rispetto alla signature degli stessi metodi all’interno della classe che realizza il componente, questi ultimi hanno come parametro di ingresso anche l’istanza del componente da renderizzare. 7. Sicurezza Per quanto riguarda la sicurezza, un’applicazione realizzata con JavaServerFaces può utilizzare il protocollo HTTPS per il trasferimento delle pagine e delle informazioni, in modo da garantire la crittografia dei dati. La gestione della sicurezza tramite l’SSL (Secure Socket Layer) è completamente indipendente dal framework ed è praticamente la medesima di una qualsiasi applicazione Web realizzata in Java. In sostanza, si fa affidamento ai meccanismi del Web Container sottostante, all’interno del quale va abilitato il supporto per le connessioni basate su SSL. Una volta effettuata questa operazioni, nell’ambito dell’applicazione è necessario modificare opportunamente il Deployment Descriptor web.xml, per poter specificare quali pagine dovranno utilizzare il protocollo HTTPS. 73 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Per questo scopo, si utilizza il tag <security-constraint>, all’interno del quale si specificano le risorse Web da trasmettere tramite SSL (tag <web-resource-collection>) ed il tipo di trasporto da utilizzare (tag <transport-guarantee>) che può essere di tre tipi : - NONE : utilizza HTTP; - INTEGRAL , CONFIDENTIAL : utilizza HTTPS; <security-constraint> <display-name>SSL Constraint</display-name> <web-resource-collection> <web-resource-name> Automatic SLL Forwarding </web-resource-name> <url-pattern>[pagina.jsp]</url-pattern> <http-method>GET</http-method> <http-method>PUT</http-method> <http-method>POST</http-method> <http-method>DELETE</http-method> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> Questo tipo di approccio evidenzia che la sicurezza è strettamente legata all’architettura sulla quale è in esecuzione l’applicazione e non dal framework con cui è stata realizzata. 8. Configurazione dell’applicazione In generale, nell’ambito di una Web application realizzata con JSF, i compiti principali dell’ Application architect sono tipicamente i seguenti : - registrare gli oggetti di back-end in modo che siano accessibili da un qualsiasi punto dell’applicazione; - configurare i backing beans (managed beans) in modo che siano istanziati in maniera corretta quando le pagine fanno riferimento ad essi; - definire la navigazione tra le pagine dell’applicazione; 74 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ - eseguire il “packaging” dell’applicazione, includendo tutte le pagine, gli oggetti e gli altri file, in modo che possa essere distribuita in un qualsiasi web container; La tecnologia JavaServer Faces mette a disposizione una modalità di configurazione dell’applicazione che è completamente portabile, mediante un documento XML tipicamente chiamato faces-config.xml. In realtà, nel caso di applicazioni di grandi dimensioni, l’applicazione può prevedere anche più di un file di configurazione, ciascuno dei quali permette di configurare una parte delle funzionalità dell’applicazione stessa. E’ da ricordare, inoltre, che l’Application developer può sempre accedere direttamente da codice al file faces-config.xml, grazie all’utilizzo della classe Application. Infatti, all’avvio dell’applicazione, un parser XML legge le informazioni del file di configurazione e le carica nell’unica istanza della classe Application prevista. 8.1 Configurazione dei Backing Beans Per istanziare i backing beans che vengono utilizzati in un’applicazione JSF ed assegnarli ad un certo ambito di visibilità (scope), è possibile utilizzare la corrispondente funzionalità offerta nel file di configurazione XML. In questo modo, quando una pagina fa riferimento ad un backing bean, l’implementazione JSF lo inizializza in maniera opportuna in accordo alle informazioni contenute nel file facesconfig.xml. Attraverso tale funzionalità, si possono ottenere i seguenti vantaggi : - registrare i beans in un unico file centralizzato, in modo da essere disponibili nell’ambito di tutta l’applicazione o comunque essere istanziati solo nel momento in cui sono necessari; - personalizzare le proprietà dei beans, direttamente nel file di configurazione senza scrivere del codice aggiuntivo in Java; - è possibile assegnare dei valori iniziali alle proprietà di un bean, nel momento in cui viene creato; 75 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ Per registrare un backing bean nel file di configurazione, si utilizza l’elemento XML <managed-bean>, all’interno del quale si utilizzano ulteriori tag per specificare : - il nome da assegnare al bean, per poterlo referenziare all’interno delle pagine (<managed-bean-name>); - la classe che implementa il bean (<managed-bean-class>); - l’ambito di visiblità (scope) all’interno dell’applicazione (<managed-beanscope>); - le relative proprietà con eventuali valori iniziali (<managed-property>); Le prime tre informazioni sono strettamente necessarie, mentre l’ultima è facoltativa, considerando che le proprietà del bean vengono comunque definite nella corrispondente classe Java. Per quanto riguarda l’ambito di visibilità, esso può essere del tipo : request, session, application e none (in questo caso il bean viene istanziato ogni volta che deve essere utilizzato). <managed-bean> <managed-bean-name>backingBean</managed-bean-name> <managed-bean-class> package.ClasseBackingBean </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> 8.2 Localizzazione, Internazionalizzazione e Messaggi Una delle caratteristiche offerte dalla tecnologia JSF è l’Internazionalizzazione (I18N), che permette di visualizzare il testo della Web application, in un linguaggio diverso in base alla localizzazione geografica dell’utilizzatore. Per fare ciò è necessario creare uno o più Resource Bundle, ciascuno dei quali contiene i messaggi da visualizzare in un certo linguaggio, tra quelli supportati dall’applicazione. In generale, i Resource Bundles non sono altro che dei file con estensione “properties”, aventi tutti lo stesso nome più il suffisso relativo al linguaggio a cui ciascuno di essi fa riferimento. Per rendere disponibile questa funzionalità all’applicazione, è necessario registrare i Resource Bundle all’interno del file faces-config.xml mediante l’elemento <message- 76 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ bundle>. Ad esso, vanno poi aggiunti il default Locale e tutti i restanti Locale supportati dall’applicazione, facendo uso di un solo elemento <default-locale> ed uno o più <supported-locale>. <application> <locale-config> <default-locale>it</default-locale> <supported-locale>en</supported-locale> </locale-config> <message-bundle>ApplicationResources</message-bundle> </application> 8.3 Registrare un Custom Validator Se l’Application developer ha realizzato dei Custom Validators per eseguire operazioni di validazione, è necessario registrarli all’interno del file di configurazione mediante l’elemento <validator>, che conterrà ulteriori tag per specificare le seguenti informazioni : - identificativo univoco del validator per referenziarlo all’interno della classe che realizza il Tag Handler (<validator-id>); - la classe che implementa il validator (<validator-class>); - gli attributi previsti dal validator, con il relativo nome ed il tipo (<attribute>); Le prime due informazioni sono necessarie, mentre l’ultima è facoltativa considerando che gli attributi del validator sono definiti nella classe che lo implementa e nel suo Tag Handler. <validator> <validator-id>idValidator</validator-id> <validator-class>package.ClasseValidator</validator-class> </validator> 8.4 Registrare un Custom Converter Così come nel caso di un validator, anche per la realizzazione di un Custom Converter, si rende necessaria la registrazione all’interno del file di configurazione, 77 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ mediante l’elemento <converter>. Quest’ultimo, a sua volta, prevede ulteriori elementi al suo interno, per specificare le seguenti informazioni : - identificativo univoco del converter per poterlo referenziare nel tag di un componente della UI che ne fa uso (<converter-id>); - la classe che implementa il converter (<converter-class>); <converter> <converter-id>idConverter</converter-id> <converter-class>package.ClasseConverter</converter-class> </converter> 8.5 Configurare le Navigation Rules Come è stato detto nel trattare il Navigation Model, la navigazione all’interno dell’applicazione è definita da un insieme di regole (navigation-rules) che permettono di determinare quale sia la pagina successiva, nel momento in cui si clicca su un bottone oppure su di un link. Tali regole sono configurate all’interno del file faces-config.xml. Ogni regola di navigazione specifica in quale direzione muoversi attraverso le pagine, a partire da una certa pagina. Una volta individuata la regola, sulla base della pagina attualmente visualizzata, si determina la pagina successiva sulla base di un outcome, ossia una stringa che viene restituita da un metodo di un backing bean, associato al componente (bottone o link) su cui si è cliccato. Ciascuna regola viene definita all’interno del file di configurazione, mediante l’elemento <navigation-rule>, il quale contiene al suo interno ulteriori tag per specificare le seguenti informazioni : - la pagina dalla quale si parte per la navigazione applicando tale regola (<fromview-id>); - le possibili pagine di destinazione, individuate attraverso dei “casi” di navigazione (<navigation-case>), ciascuno dei quali specifica al suo interno : l’outcome (<from-outcome>) oppure una action (<from-action>) e la pagina stessa di destinazione (<to-view-id>); 78 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ <navigation-rule> <from-view-id>/pagina.jsp</from-view-id> <navigation-case> <from-outcome>nomeOutcome</from-outcome> <to-view-id>/paginaDestinazione.jsp</to-view-id> </navigation-case> ... ... </navigation-rule> 8.6 Registrare un Custom Renderer in un Renderer Kit Un Renderer Kit definisce un insieme di classi Renderer che vengono utilizzate per poter eseguire la visualizzazione di uno o più componenti della UI, in maniera diversa in base al dispositivo client con cui si accede alla Web application. Nel caso in cui l’Application developer crea un Custom Renderer, per la visualizzazione di un certo componente, è necessario che venga assegnato ad un Renderer Kit, mediante la registrazione nel file di configurazione. L’elemento XML adottato è <renderer-kit>, il quale prevede al suo interno uno o più tag <renderer>, ciascuno dei quali contiene le informazioni seguenti relative ad uno specifico renderer : - la famiglia del componente della UI a cui è associato il renderer (<componentfamily>); - il tipo del renderer (<renderer-type>), che viene utilizzato dal Tag Handler associato al componente, per determinare quale renderer applicare su di esso; - la classe che implementa il renderer (<renderer-class>); - gli attributi eventuali del renderer (<attribute>) con il proprio nome (<attribute-name>) e la classe (<attribute-class>); In generale, non è obbligatorio specificare il Renderer Kit di appartenenza di un Custom Renderer; in questo caso, esso sarà assegnato a quello di default HTML, fornito con l’implementazione JSF. 79 Capitolo II – Il framework JavaServer Faces _________________________________________________________________ <render-kit> <renderer> <component-family>FamigliaComponente</component-family> <renderer-type>TipoRenderer</renderer-type> <renderer-class>package.ClasseRenderer</renderer-class> </renderer> ... ... </render-kit> 8.7 Registrare un Custom Component Oltre alla registrazione di un Custom Renderer, si rende ovviamente necessaria anche la registrazione del Custom Component a cui esso è associato, mediante l’elemento <component>. Per ciascun componente, vengono specificate le seguenti informazioni : - il tipo del componente (<component-type>); - la classe che implementa il componente (<component-class>); - le proprietà del componente (<property>), per ciascuna delle quali è definito il nome (<property-name>) e la classe (<property-class>); Tipicamente, le prime due informazioni sono necessarie, mentre le successive sono facoltative, in quanto tali proprietà vengono comunque definite all’interno della classe che implementa il componente. <component> <component-type>TipoComponente</component-type> <component-class>package.ClasseComponente</component-class> </component> 80 Capitolo III – Il framework Struts _________________________________________________________________ Capitolo III – Il framework Str uts 1 . Introduzione Struts è un framework OpenSource per la realizzazione di Web application secondo il pattern MVC (Model – View – Controller), caratterizzato da una serie di classi e dalle relative API (Application Program Interface) che permettono di : - acquisire i dati dei form riempiti dall’utente durante la navigazione ed eseguire le elaborazioni su di essi; - individuare gli handlers (gestori) , detti anche actions, per esaudire ciascuna richiesta; - definire la navigazione fra le pagine all’interno della Web application; - supportare l’internazionalizzazione e l’accessibilità; Inoltre, include i due seguenti “plug-in” : - Tiles : framework per la realizzazione del layout delle pagine con “mattonelle” (tiles) componibili; - Validator : plug-in che fornisce un vasto insieme di validatori, per la validazione dei dati immessi dall’utente nei form; Infine, mette a disposizione tre librerie di tag (Tag Libraries) : - Struts HTML : per la realizzazione dell’interfaccia utente, con una serie di tag che permettono di introdurre i componenti della UI all’interno di ciascuna pagina; - Struts Bean : per poter accedere, direttamente da ciascuna pagina, alle proprietà dei JavaBeans che definiscono il Model dell’applicazione; - Struts Logic : per definire dei costrutti di controllo (condizionali e ciclici) che permettono la costruzione dinamica di una pagina; 81 Capitolo III – Il framework Struts _________________________________________________________________ Ciò che rende Struts uno degli strumenti maggiormente utilizzati per lo sviluppo di Web application, è soprattutto il fatto di essere OpenSource. Infatti, milioni di sviluppatori in tutto il mondo partecipano al miglioramento del framework, realizzando classi aggiuntive che ne aumentano le potenzialità oppure migliorando quelle preesistenti. Nel tempo, tutto ciò ha reso Struts un strumento stabile e fortemente maturo con un curva di apprendimento (learning curve) piuttosto bassa. Infine, il vantaggio principale che scaturisce dall’utilizzo di Struts, è la netta separazione tra l’Application processing layer (Model) ed il Presentation layer (View), favorendo il team-working. Esso si basa fortemente sugli strumenti che costituiscono principalmente un’applicazione Web realizzata in Java : le pagine JSP e le Servlet. Infatti, il flusso di esecuzione all’interno di ciascuna Web application è gestito attraverso una Servlet, in esecuzione nel Web Container. Ovviamente, alle numerose classi che costituiscono il framework, è possibile aggiungere ulteriori classi che definiscono la business-logic, tipicamente realizzate attraverso dei JavaBeans. La semplicità di configurazione della Web application da realizzare è garantita dalla presenza di un file XML (struts-config.xml), nel quale vengono impostati tutti i parametri e gli elementi costitutivi dell’applicazione stessa. Infine, dalla versione 1.1, è stata introdotta la possibilità di realizzare un’applicazione di grandi dimensioni suddividendola in moduli, ciascuno dei quali definisce una parte delle funzionalità complessive ed ha un proprio file di configurazione autonomo. In questo modo, si può gestire ciascun modulo separatamente dagli altri ma è ovviamente garantita la comunicazione fra di essi. Tutti gli elementi che costituiscono il framework sono raggruppati in otto principali package : - action, actions : contengono le classi che costituiscono il Controller e le classi che possono essere usate o estese all’interno della propria applicazione; - config : contiene le classi che definiscono una rappresentazione “in memoria”, della configurazione dell’applicazione specificata nel file struts-config.xml; - taglib : contiene i Tag Handler, ossia le classi per la gestione delle librerie di tag di Struts; 82 Capitolo III – Il framework Struts _________________________________________________________________ - tiles : include le classi utilizzate dal framework Tiles; - upload : contiene le classi per eseguire l’upload di file attraverso un browser Web; - util : include delle classi general-purpose, con delle utility sfruttate dal framework; - validator : contiene le classi specifiche di Struts per lo sviluppo di validatori personalizzati; Figura 1 - Packages Architettura Struts 2. Controller, Model and View Components Basandosi sul pattern MVC (Model – View – Controller), è ovvio che Struts è caratterizzato da una serie di componenti e di classi, che compongono ciascuna delle tre parti del pattern stesso : - Controller : le classi che gestiscono il flusso di esecuzione dell’applicazione e garantiscono la comunicazione tra il Model e la View; ad esse si aggiungono anche una serie di classi di utilità (Utility Classes); 83 Capitolo III – Il framework Struts _________________________________________________________________ - Model : le classi che definiscono lo stato dell’applicazione e le funzionalità della business-logic; - View : tipicamente le pagine JSP che utilizzano i tag delle librerie di Struts, per la realizzazione dell’interfaccia utente (UI); 2.1 Controller Components Il Controller di Struts si basa sul pattern “Front Controller” della J2EE (Java2 Enterprise Edition), in modo da poter gestire tutte le richieste in maniera centralizzata. Oltre ai numerosi vantaggi relativi alle funzionalità dell’applicazione, ne scaturisce che alcuni servizi come la sicurezza, l’internazionalizzazione ed il logging sono concentrati nel Controller. Facendo riferimento al pattern MVC, il Controller di Struts ha i seguenti compiti : - ricevere tutte le richieste provenienti dai vari client; - per ciascuna richiesta, determinarne il gestore, detto anche action, che avrà il compito di eseguire le elaborazioni richieste, utilizzando anche i JavaBeans che costituiscono la business-logic; - raccogliere i risultati delle elaborazioni per renderli disponibili al client; - determinare la View alla quale passare il controllo per la visualizzazione dei risultati; Figura 2 - Pattern MVC 84 Capitolo III – Il framework Struts _________________________________________________________________ Struts prevede una serie di classi, legate fra loro, per la strutturazione del Controller. Figura 3 - Class Diagram Controller Components 2.1.1 ActionServlet La classe ActionServlet rappresenta la Servlet in esecuzione nel Web Container, alla quale arrivano tutte richieste dei client, dirette all’applicazione stessa. Quando la classe riceve una request di tipo HTTP (HttpRequest), esegue uno dei metodi tipici di una Servlet, ossia doGet() oppure doPost(), in base al metodo che è stato utilizzato per inviare la richiesta. Ciascuno dei due metodi invoca il metodo process(), il quale si occupa di eseguire le principali operazioni che competono alla ActionServlet, tra cui individuare il modulo della Web application al quale è destinata la richiesta, se l’applicazione prevede una suddivisione in più moduli. Il framework da la possibilità allo sviluppatore di realizzare una classe che estenda ActionServlet, per poter definire un Controller personalizzato, che va comunque registrato nel Deployment Descriptor web.xml, per segnalare al Web Container quale Servlet avviare per gestire l’applicazione. Struts prevede una particolare fase di inizializzazione, nel momento in cui la prima richiesta giunge alla Web application. In questa fase, ovviamente, viene istanziata la ActionServlet ed invocato su di essa il metodo init(), nell’ambito del quale, la Servlet esegue le seguenti operazioni : - inizializza il ResourceBundle interno del framework con i messaggi per la creazione dei files di log; 85 Capitolo III – Il framework Struts _________________________________________________________________ - carica i parametri di configurazione della Servlet dal file web.xml; - carica ed inizializza il nome ed il mapping della Servlet; - carica le informazioni di configurazione del modulo principale dell’applicazione, dal file struts-config.xml, creandone una copia in memoria nella classe ApplicationConfig, memorizzata nel ServletContext (contesto di esecuzione della Servlet); - carica ogni ResourceBundle assegnato al modulo di default e contenente i messaggi da visualizzare all’utente; - carica le informazioni relative a ciascun data source specificato nel file di configurazione; - inizializza ed avvia i plug-in assegnati al modulo principale; - eseguite tutte le operazioni suddette per il modulo di default, esse vengono ripetute per tutti i moduli componenti l’applicazione; Figura 4 - Operazioni di inizializzazione della ActionServlet 2.1.2 RequestProcessor Dalla versione 1.1 del framework, alla classe ActionServlet è stata affiancata la classe RequestProcessor. Si è resa necessaria tale introduzione, in virtù del fatto che, 86 Capitolo III – Il framework Struts _________________________________________________________________ proprio da tale versione, è stato introdotto il supporto di realizzazione di un’applicazione in più moduli separati. In questo caso, a partire dalla classe ActionServlet, vengono create più istanze della classe RequestProcessor, quanti sono i moduli dell’applicazione, in modo tale che ciascuno di essi abbia il proprio request handler, per la gestione delle richieste in ingresso. Nel momento in cui arriva una richiesta alla ActionServlet, quest’ultima determina il modulo a cui essa è destinata ed istanzia la relativa classe RequestProcessor, verso la quale inoltra la richiesta e ne invoca il metodo process(). All’interno di tale metodo, vengono eseguite le seguenti operazioni : - vengono caricate le informazioni per la gestione dei form “multipart”, qualora la richiesta preveda l’upload di un file; - viene determinato il path della richiesta; - è individuato il Locale, per gestire l’internazionalizzazione; - viene invocato il metodo processPreprocess(), all’interno del quale vengono eseguite le operazioni preliminari all’invocazione della action che dovrà gestire la richiesta. Tipicamente, se lo sviluppatore crea un proprio request handler, estendendo RequestProcessor, inserisce all’interno di questo metodo tutti i controlli che servono per valutare se la sessione corrente dell’utente è valida; - viene determinato il mapping delle action, per poter individuare quale di queste dovrà gestire la richiesta; - vengono eseguiti i metodi per : individuare il formbean associato alla action selezionata, caricarlo nello scope specificato nel file di configurazione, popolarlo con i dati immessi dall’utente e validarlo; - viene invocata la action per la gestione della richiesta; - terminata l’esecuzione della action, viene eseguito il forward verso la View per la visualizzazione dei risultati; 87 Capitolo III – Il framework Struts _________________________________________________________________ Nel caso di realizzazione di un proprio RequestProcessor, la signature del metodo processPreprocess() soggetto ad overriding è la seguente : protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) { ... } Si osservi il parametro di ingresso relativo alla request che deve essere gestita, dal quale poter ricavare eventuali informazioni da controllare prima di una successiva elaborazione. 2.1.3 Action La classe Action può essere considerata il “cuore” del framework. Essa definisce un “bridge” (ponte) tra il Controller ed il Model, in virtù del fatto che riceve dal primo la richiesta da esaudire ed utilizza le classi del secondo per espletarla. Per la gestione di una richiesta da parte di una action, viene invocato su di essa il metodo execute(), all’interno del quale vengono istanziati gli oggetti che compongono la business-logic ed eseguono l’elaborazione richiesta. Tale metodo restituisce un oggetto della classe ActionForward, che specifica la direzione verso la quale incanalare il flusso dell’applicazione. Al termine dell’esecuzione di tale metodo, il controllo ritorna al Controller che determina, sulla base dell’oggetto ActionForward, a quale View compete la visualizzazione. public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ... } Si osservi che il metodo execute() riceve in ingresso l’oggetto ActionMapping che contiene il mapping delle action dell’applicazione e serve per individuare i forward, l’oggetto ActionForm che contiene i dati del form trasmesso dall’utente ed infine, gli oggetti HttpServletRequest ed HttpServletResponse relativi alla richiesta ed alla risposta. 88 Capitolo III – Il framework Struts _________________________________________________________________ La classe Action è definita “thread-safe”, nel senso che, nell’ambito di un’applicazione, viene creata sempre e solo un’unica istanza di ciascuna action. Ciò vuol dire che tutti i client condividono la medesima istanza di ciascuna di esse e possono invocare sulla medesima action il metodo execute() contemporaneamente, proprio perché la gestione avviene con thread separati. L’elenco delle action istanziate è contenuto all’interno di una HashMap della classe RequestProcessor. In virtù dell’approccio “thread-safe” con cui vengono gestite le action, è consigliabile non utilizzare variabili di istanza per ciascuna di esse, che altrimenti verrebbero condivise da tutti i client. Inoltre, il framework mette a disposizione delle particolari classi che estendono il funzionamento della classe Action : - ForwardAction; - DispatchAction; - LookupDispatchAction; - SwitchAction: 2.1.3.1 ForwardAction In moltissime situazioni, c’è la necessità di passare da una pagina JSP all’altra, senza il bisogno di utilizzare una Action. In generale, invocare direttamente una pagina JSP è un approccio non consigliato per molteplici motivazioni. Il Controller ha il compito di selezionare opportunamente il modulo dell’applicazione a cui è diretta una richiesta per poterne caricare la configurazione ed i messaggi del ResourceBundle. Se questa fase viene saltata, si corre il rischio che non vengano visualizzati all’utente i messaggi corretti. Un’altra motivazione è puramente concettuale, nel senso che, chiamare direttamente una pagina JSP viola il pattern MVC, poiché non si sfrutta il Controller. Per risolvere questa problema, è stata introdotta la classe ForwardAction che permette di eseguire il forward da una pagina all’altra, passando attraverso il Controller. 89 Capitolo III – Il framework Struts _________________________________________________________________ 2.1.3.2 DispatchAction - LookupDispatchAction In moltissimi casi, un’applicazione Web è caratterizzata da una serie di funzionalità che sono più o meno logicamente correlate. La soluzione di base sarebbe quella di definire più action separate, ciascuna delle quali svolga una delle funzionalità, oppure definire un’unica action ed all’interno del metodo execute() gestire le diverse funzionalità sulla base dei parametri della request. La migliore soluzione può essere realizzata utilizzando la classe DispatchAction, all’interno della quale è possibile avere più metodi, con la stessa signature del metodo execute(), che implementano le diverse funzionalità. public class ClasseAction extends DispatchAction { ... public ActionForward metodo1(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ... } public ActionForward metodo2(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { ... } ... ... } Per poter distinguere l’invocazione di un metodo da un altro, alla action viene assegnato un parametro specificato all’interno del file di configurazione mediante l’attributo parameter. Di volta in volta, tale parametro può assumere un valore diverso, tipicamente il nome di uno dei metodi della classe DispatchAction, in modo tale da utilizzare una delle funzionalità implementate. <action input="pagina.jsp" name="nomeFormBean" parameter="dispatch" path="/nomeAction" scope="request" type="package.ClasseAction"> <forward name="nomeForward1" path="pagina1.jsp"/> ... <forward name="nomeForwardN" path="paginaN.jsp"/> </action> 90 Capitolo III – Il framework Struts _________________________________________________________________ In conclusione, ciascuna request, destinata alla action, dovrà specificare il nome di quest’ultima, seguito dal parametro con il suo relativo valore, per invocare uno dei metodi interni alla classe. <html:form action="/nomeAction.do?dispatch=metodo1"> ... </html:form> Un ulteriore miglioramento è stato introdotto dalla classe LookupDispatchAction, la quale prevede il metodo getKeyMethodMap(), che restituisce un oggetto della classe Map, facendo corrispondere ad un certo messaggio del ResourceBundle, il nome di uno dei metodi della classe. In questo modo, la request non deve necessariamente assegnare al parametro suddetto il nome del metodo da invocare, ma anche eventualmente un messaggio del ResourceBundle. 2.1.3.3 SwitchAction La classe SwithcAction è stata introdotta dalla versione 1.1 del framework, in concomitanza con la nuova potenzialità di poter sviluppare le applicazioni di grandi dimensioni suddividendole in moduli separati. Tale classe, infatti, permette di passare da un modulo all’altro ed in particolare raggiungere una delle risorse del modulo scelto. Per fare questo, la action va invocata con due parametri : - prefix : definisce il nome del modulo verso cui spostare il controllo; - page: l’URI della risorsa a cui accedere, all’interno del modulo scelto; /switch.do?page=[pagina.jsp]&prefix=[prefissoModulo] 2.1.4 ActionForward Come visto in precedenza, il metodo execute() della classe Action, che gestisce una certa richiesta, fornisce come parametro di uscita un oggetto della classe 91 Capitolo III – Il framework Struts _________________________________________________________________ ActionForward. Tale classe rappresenta un’astrazione logica di una risorsa Web, che può essere una pagina JSP, una Servlet oppure eventualmente un’altra action. Essa è “costruita” (wrapper) intorno a ciascuna risorsa, in modo da disaccoppiare l’applicazione dalla locazione fisica delle stessa, che viene specificata nel file di configurazione. L’oggetto della classe ActionForward viene restituito dal metodo execute() della action, utilizzando il metodo findForward() della classe ActionMapping. Passando a tale metodo il nome con cui l’ActionForward è mappato nel file strutsconfig.xml, viene determinata la risorsa verso la quale inoltrare il flusso dell’applicazione. Un oggetto ActionForward può essere creato anche direttamente all’interno del codice, mediante il suo costruttore, specificando l’URL verso il quale eseguire il forwarding. 2.2 Utility Classes Nell’ambito della realizzazione di una Web application, ci sono molto spesso una serie di operazioni che vanno ripetute più volte. Tale situazione si verifica anche con il framework Struts, il quale raggruppa tutte queste funzionalità in opportune classi di utilità, in modo da poter essere condivise fra più componenti ed utilizzate da più applicazioni. Tali classi sono raggruppate in package separati e le principali sono le seguenti : - classe RequestUtils : mette a disposizione una serie di metodi per gestire ciascuna richiesta pervenuta alla Web application; - classe ResponseUtils : ha un ruolo simile alla classe precedente, con la differenza che è orientata alla gestione delle response; - commons-package BeanUtils : per la gestione dei JavaBeans definiti all’interno dell’applicazione, in particolare per popolare i formbean con i dati immessi dall’utente ed accedere alle relative proprietà; 92 Capitolo III – Il framework Struts _________________________________________________________________ 2.3 Model Components Nell’ambito del pattern MVC, il Model è caratterizzato da tutte le classi che costituiscono la business-logic ed implementano le funzionalità offerte dall’applicazione. Tali componenti devono essere completamente indipendenti dal tipo di framework che viene adottato, in virtù del fatto che in una architettura a livelli, i livelli superiori devono essere legati a quelli inferiori, ma non vale il viceversa. Nel momento in cui, all’interno delle classi della business-logic, utilizziamo elementi del framework, si viola tale modello e si determina un accoppiamento tra due livelli che devono essere indipendenti. Figura 5 - Dipendenza dei livelli All’interno del Model, si definiscono i cosiddetti business-object (BO), i quali non rappresentano nient’altro che delle astrazioni software delle entità del mondo reale. Affinché una classe possa essere considerata un business-object, è necessario che abbia le seguenti caratteristiche : - mantenga uno stato e definisca un comportamento; - rappresenti una entità del dominio del problema; - sia riutilizzabile; Strutturando il Model in questo modo, esso diventa assolutamente indipendente dal framework che verrà adottato per realizzare la Web application. In alcuni casi, si potrebbe pensare di utilizzarlo anche per lo sviluppo di un’applicazione desktop e non orientata al Web. 93 Capitolo III – Il framework Struts _________________________________________________________________ In relazione al framework Struts, quest’ultimo non vincola lo sviluppatore nel dover realizzare il Model con una particolare tecnologia. I principali strumenti adottati per la realizzazione dei business-object, sono tipicamente gli Enterprise JavaBeans (EJB) oppure i più semplici JavaBeans (JB), fortemente affermati nella programmazione Java. Mediante questi strumenti, è possibili sviluppare le classi del Model definendone sia i dati (lo stato dell’applicazione) che i metodi (il comportamento). Infine, per garantire la persistenza delle informazioni, è necessario fare in modo che tali oggetti siano associati ad una base di dati. La mappatura tra i due elementi, può essere realizzata in due modi principali : - JDBC : si realizzano ulteriori classi che sfruttano le funzionalità dei driver JDBC (Java DataBase Connectivity) per accedere alle basi di dati. In questo modo, ciascun oggetto della business-logic utilizza l’oggetto corrispondente JDBC per garantirsi la propria persistenza; - ORM Frameworks : si utilizzano dei particolari frameworks, detti ORM – Object to Relational Mapping, i quali eseguono la mappatura in maniera automatica. Tra i più importanti è da ricordare Hibernate. 2.4 View Components In generale, la View rappresenta la visualizzazione del Model dell’applicazione, secondo una certa interfaccia utente. Ciò vuol dire che possono esistere più View differenti a dispetto di un unico Model. Tipicamente, nell’ambito di Struts, le differenti Views vengono realizzate attraverso le pagine JSP, sfruttando le diverse librerie di tag che il framework mette a disposizione. Un ruolo fondamentale, nell’interazione con l’utente, è giocato dalla classe ActionForm, che permette di definire i formbean mediante i quali vengono raccolti i dati che l’utente immette attraverso i form dell’applicazione. 94 Capitolo III – Il framework Struts _________________________________________________________________ 2.4.1 ActionForm Generalmente, ogni Web application prevede l’interazione con l’utente e quindi l’immissione, da parte di quest’ultimo attraverso dei form costituiti da uno o più campi, di dati su cui verranno eseguite le elaborazioni. Una volta che i dati vengono trasmessi, l’applicazione deve farsi carico di acquisirli, validarli ed in caso di errore visualizzare un messaggio all’utente ed infine, di elaborarli. Per poter eseguire tutte queste operazioni nel modo più semplice possibile, Struts mette a disposizione la classe ActionForm, la quale ha come compito principale quello di raccogliere i dati dai form riempiti dall’utente e metterli a disposizione di una action, per eseguirne l’elaborazione. Ulteriore funzionalità fornita da questa classe è la validazione mediante la quale è possibile controllare che i dati immessi dall’utente siano corretti ed, in caso di esito negativo, segnalare gli errori a quest’ultimo. In generale, non bisogna dichiarare un formbean per ogni form HTML previsto dall’applicazione, perché lo stesso formbean può essere condiviso ed associato a più action. Ovviamente, è necessario definirne anche l’ambito di visibilità , che può essere di due tipi : - request : i dati del formbean sono memorizzati fino al completamento del ciclo request-response, dopodichè viene cancellato; - session : i dati del formbean sono disponibili durante tutta la sessione utente; Ciascun formbean prevede un particolare ciclo di vita, nell’ambito del quale si possono distinguere le seguenti fasi : - la richiesta arriva al Controller con i dati di un certo form; - viene creato un nuovo formbean oppure riciclato quello esistente; - viene invocato il metodo reset() che annulla un eventuale contenuto precedente del formbean; - il formbean viene memorizzato nell’ambito di visibilità (scope), specificato nel file di configurazione; - i dati della richiesta vengono trasferiti nel formbean, che viene così popolato; 95 Capitolo III – Il framework Struts _________________________________________________________________ - viene eseguita la validazione dei dati, solo se espressamente specificata nel file di configurazione; - se ci sono errori, il controllo ritorna alla pagina di input dei dati altrimenti viene invocato il metodo execute() della action, a cui il formbean è associato, per elaborare i dati; Figura 6 - Ciclo di vita della ActionForm La classe ActionForm è , però, una classe astratta, per cui è necessario implementare una propria classe che la estenda per poter definire un formbean necessario all’interno dell’applicazione. Tale classe avrà al suo interno una serie di proprietà che corrispondono ai campi del form ed i relativi metodi getter e setter, per accedere ad esse. Inoltre, è necessario eseguire l’overriding dei due seguenti metodi : - reset() : per poter resettare il contenuto del formbean; - validate() : per poter eseguire la validazione dei dati contenuti nel form e restituire un oggetto ActionErrors con eventuali messaggi di errore, se la validazione non ha avuto esito positivo; 96 Capitolo III – Il framework Struts _________________________________________________________________ public void reset(ActionMapping mapping, HttpServletRequest request) { ... } public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ... } Ogni formbean realizzato va, inoltre, dichiarato all’interno del file di configurazine XML ed assegnata, sempre all’interno di quest’ultimo, alle action che potranno eseguire sui dati le corrispondenti elaborazioni. <form-bean name="nomeFormBean" type="package.ClasseActionForm"/> E’ da sottolineare, che la signature del metodo execute() di una action, prevede tra i parametri di ingresso, proprio il formbean con i dati su cui andranno eseguite le operazioni della business-logic. 2.4.2 ActionErrors In generale, i dati che vengono immessi dall’utente all’interno di un form della Web application, sono sottoposti ad un’operazione di validazione. L’esito di quest’ultima non è sempre positivo ed , in caso di errori, è necessario visualizzare all’utente dei messaggi di errore, per segnalare che i dati immessi non sono corretti. Tali errori vengono memorizzati nel sistema, mediante la classe ActionErrors. Tipicamente, nel momento in cui è richiesta la validazione dei dati, viene invocato il metodo validate() del formbean, derivato da ActionForm. Se durante la validazione si verificano uno o più errori, viene creato un oggetto ActionErrors, il quale altro non è che una lista, all’interno della quale possono essere salvati tali errori con i relativi messaggi da visualizzare all’utente. La classe prevede il metodo add(), mediante il quale si può aggiungere un messaggio ed associarlo ad un elemento della View, in cui verrà eseguita la visualizzazione. 97 Capitolo III – Il framework Struts _________________________________________________________________ Le classi che permettono di creare dei messaggi di semplice segnalazione o di errore all’utente sono le seguenti : - ActionMessage: - ActionError; 2.4.2.1 ActionMessage – ActionError Il framework Struts prevede la classe ActionError, per creare dei messaggi di warning o di errore da visualizzare all’utente. Dalla versione 1.1, è stata aggiunta la classe ActionMessage, per un motivo puramente concettuale. Infatti, facendo uso soltanto della classe ActionError, anche un messaggio qualsiasi, non di errore, verrebbe interpretato come tale, dallo sviluppatore che legge o crea il codice. Per poter distinguere un errore, da un semplice messaggio da visualizzare nella View, è stata introdotta la classe ActionMessage, la quale altro non è che una generalizzazione della ActionError. Ovviamente, è stata aggiunta anche la classe ActionMessages che, alla pari della classe ActionErrors, si fa carico di memorizzare più messaggi all’interno di una lista. Figura 7 - Class Diagram Messages/Errors 98 Capitolo III – Il framework Struts _________________________________________________________________ 2.4.3 DynaActionForm Uno dei problemi che sorge nell’utilizzo della classe ActionForm per la realizzazione dei formbean, è il numero elevato di classi da creare e da aggiungere al proprio progetto software. Infatti, se i form HTML della Web application sono numerosi, altrettanto numerose saranno le classi che dovranno derivare da ActionForm e realizzare i formbean relativi. Per risolvere questo problema, alcuni sviluppatori creano un unico formbean, le cui proprietà corrispondono a tutti i campi di tutti i form HTML Un’altra conseguenza dell’uso di più classi ActionForm, deriva dal fatto che, se vengono aggiunti o rimossi dei campi ad uno o più form HTML, c’è bisogno di modificare anche le classi dei formbean corrispondenti e ricompilarle. Per questi motivi, è stata introdotta la possibilità di realizzare dei formbean dinamici facendo uso della classe DynaActionForm, la quale altro non è che un’estensione della classe ActionForm. Figura 8 - Class Diagram DynaActionForm/ActionForm Le differenze sostanziali fra queste due classi, riguardano i tre seguenti aspetti : - le proprietà del formbean; - il metodo validate(); - il metodo reset(); 99 Capitolo III – Il framework Struts _________________________________________________________________ Le proprietà di un formbean dinamico sono definite all’interno del file di configurazione struts-config.xml. Durante l’esecuzione della Web application, il framework crea un’istanza della classe e mette a disposizione dei metodi getter e setter per accedere a tali proprietà. La modifica oppure l’introduzione di uno o più proprietà, prevede l’intervento sul file di configurazione e tutto ciò garantisce grande potenza e flessibilità. <form-bean name="nomeFormBeanDinamico" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="porprieta1" type="ClasseProprieta1"/> ... <form-property name="proprietaN" type="ClasseProprietaN"/> </form-bean> Il metodo reset() è invocato allo stesso modo della classe ActionForm, con la differenza che non si ha un maggior controllo delle operazioni che vanno eseguite al suo interno. Il comportamento di default del metodo, prevede di assegnare alle proprietà del formbean dinamico, dei valori iniziali che vengono sempre specificati all’interno del file dello stesso file di configurazione. Qualora si voglia modificare tale comportamento, è necessario realizzare una classe che estenda DynaActionForm e quindi eseguire l’overriding del metodo reset(). Infine, la validazione di un formbean dinamico potrebbe essere eseguita realizzando una classe che estenda DynaActionForm ed esegua l’overriding del metodo validate(). In generale, per evitare lo sviluppo di classi aggiuntive, si fa uso del plug-in Validator, il quale mette a disposizione una serie di regole di validazione già pronte per l’uso. 2.4.4 Tag Libraries Nell’ambito della View, oltre alle classi che permettono di gestire tutto ciò che riguarda l’interazione e lo scambio dei dati tra utente ed applicazione, rientrano anche le librerie di tag che Struts mette a disposizione per la realizzazione della UI. Queste ultime, non soltanto offrono la possibilità di introdurre gli elementi grafici in una pagina JSP, ma ne permettono anche la costruzione dinamica, oltre alla possibilità di accedere alle proprietà dei beans previsti dall’applicazione. 100 Capitolo III – Il framework Struts _________________________________________________________________ La libreria Struts HTML mette a disposizione tutti gli elementi che permettono di realizzare i form e che definiscono quella che è la grafica dell’applicazione. Il limite principale del framework è dettato dalla non indipendenza dal dispositivo con cui si accede alla Web application, per cui viene fornita una libreria che permette di definire l’interfaccia utente solo ed esclusivamente per un browser Web. La libreria Struts Bean dispone di tutti i tag per poter visualizzare e modificare i valori delle proprietà dei bean previsti dall’applicazione. Ciascun tag ha sempre gli attributi che permettono di identificare il bean e la proprietà di interesse, separatamente l’uno dall’altro. Non è supportato l’utilizzo dell’Expression Language, per accedere alle proprietà di un bean con la consueta “dot notation”, tipica della programmazione ad oggetti. Per usufruire di questa funzionalità, è necessario utilizzare la versione evoluta di questa libreria oppure fare affidamento alle librerie JSTL. Infine, la libreria Struts Logic fornisce una serie di tag che permettono di definire dei costrutti logici e condizionali all’interno di una pagina JSP, per poter costruire il contenuto di quest’ultima in maniera dinamica e sulla base di particolari condizioni. Anche in questo caso, la specifica delle condizioni viene eseguita attraverso gli attributi dei tag che non supportano l’EL, mediante il quale le operazioni diventerebbero notevolmente più semplici, potendo utilizzare gli operatori logici del linguaggio Java. 3. Ciclo di vita di una richiesta Il ciclo di vita di una richiesta ha una struttura standard, tipica del pattern MVC. Il client invia la richiesta al Web Container, il quale si fa carico di smistarla verso la ActionServlet che gestisce l’applicazione. Quest’ultima utilizza l’istanza della classe RequestProcessor, alla quale trasferisce la richiesta sottoposta al server. In memoria, è prevista l’immagine del file di configurazione strutturata mediante una serie di classi, attraverso le quali viene eseguita l’operazione di action mapping, ossia individuare quale sia la action che deve occuparsi della gestione della richiesta. Inoltre, vengono istanziati, popolati e validati i formbean che contengono i dati della richiesta. A questo punto, termina il primo intervento del Controller, il quale invoca la action individuata per permettere l’esecuzione delle elaborazioni richieste. La action si pone come un “bridge” tra Controller e Model, utilizzando i formbean, 101 Capitolo III – Il framework Struts _________________________________________________________________ contenenti i dati da elaborare, e gli oggetti delle business-logic. Al termine dell’esecuzione della action, viene restituito il forward attraverso il quale, il Controller, determina la pagina JSP della View verso la quale far proseguire la navigazione. Figura 9 - Ciclo di vita di una richiesta 4. Exception Handling Prima delle versione 1.1, il framework Struts forniva un meccanismo di gestione delle eccezioni piuttosto semplice, che spingeva gli sviluppatori a realizzare soluzioni differenti per poter gestire le condizioni di errore nelle proprie applicazioni. Dalla versione 1.1, è stato introdotto un meccanismo non molto ampio ma abbastanza efficiente, che permette di gestire le eccezioni in due modalità : - dichiarativa : le eccezioni, che possono essere sollevate nell’ambito dell’applicazione, sono “dichiarate” all’interno del file di configurazione e gestite in maniera automatica dal framework; - programmatica : le eccezioni sono gestite direttamente nel codice delle action, nella modalità consueta del linguaggio Java; La gestione delle eccezioni viene eseguita mediante l’utilizzo della classe ExceptionHandler, della quale Struts ne fornisce una versione di base ma con la 102 Capitolo III – Il framework Struts _________________________________________________________________ possibilità di estenderla, per poter realizzare un gestore delle eccezioni personalizzato. Tale classe è dotata del metodo execute(), all’interno del quale viene creato un oggetto ActionError, per contenere le informazioni riguardanti l’errore verificatesi e restituisce un oggetto ActionForward che specifica la risorsa verso la quale proseguire la navigazione per segnalare all’utente il verificarsi dell’eccezione. 4.1 Approccio Dichiarativo Nel caso in cui un’eccezione non sia gestita in maniera programmatica all’interno di una action, il RequestProcessor si fa carico di verificare se essa sia dichiarata all’interno del file di configurazione, utilizzando la classe ExceptionConfig. Il contenuto di ques’ultima è definito in fase di start-up dell’applicazione, quando viene eseguito il parsing del file struts-config.xml e le eccezioni dichiarate vengono caricate in memoria. La classe RequestProcessor prevede il metodo processException(), all’interno del quale vengono individuate le informazioni relative all’eccezione che è stata sollevata e viene istanziato l’ExceptionHandler per la sua gestione. Figura 10 - Gestione delle eccezioni Uno dei vantaggi principali dell’approccio dichiarativo riguarda soprattutto la gestione delle eccezioni che si verificano al tempo di esecuzione (runtime). Tali eccezioni, facenti parte della classe RuntimeException, sono notoriamente 103 Capitolo III – Il framework Struts _________________________________________________________________ “unchecked”, ossia non controllate e non possono essere gestite mediante i tipici blocci try-catch-finally. In questi casi, per evitare che l’applicazione si blocchi fornendo un messaggio poco user-friendly all’utente, è possibile dichiarare un’eccezione globale di questo tipo nel file di configurazione, assegnandole una pagina verso la quale redirezionare l’utente, per segnalargli il problema in maniera più “amichevole”. <global-exceptions> <exception bundle="ApplicationResources" key="chiaveMessaggioErrore" path="/paginaErrore.jsp" type="java.lang.RuntimeException"/> </global-exceptions> 4.2 Approccio programmatico Un approccio alternativo a quello dichiarativo, è l’approccio programmatico. Quest’ultimo prevede che la gestione delle eccezioni venga eseguita completamente attraverso il codice all’interno di una action e non preveda l’utilizzo di informazioni del file di configurazione. Ovviamente, tale approccio rende la gestione molto più complessa, in quanto è lo sviluppatore che deve occuparsi di catturare un’eccezione, salvare i messaggi di errore negli oggetti ActionError e produrre gli oggetti ActionForward per far proseguire la navigazione. I due tipi di approcci non sono mutuamente esclusivi, ma viceversa possono essere utilizzati contemporaneamente. Infatti, la prima opportunità di catturare un’eccezione è all’interno della classe Action che la solleva e, nel caso in cui non se ne sia prevista la gestione, essa viene catturata nel metodo processActionPerfom() della classe RequestProcessor, la quale proseguirà con l’approccio dichiarativo. 4.3 ModuleException Il framework mette a disposizione una particolare classe, nota come ModuleException, per sollevare delle eccezioni che possano poi essere gestite mediante l’approccio dichiarativo. Infatti, all’interno del metodo execute() di una action, qual’ora si verificasse una condizione di errore, è possibile sollevare un’eccezione di questo 104 Capitolo III – Il framework Struts _________________________________________________________________ tipo, che in maniera completamente automatica crea l’oggetto ActionError nel quale è possibile specificare il messaggio da visualizzare all’utente. ... ModuleException me = new ModuleException("chiaveMessaggioErrore"); throw me; ... Tipicamente, tale eccezione sarà dichiarata all’interno del file di configurazione, per cui sarà il RequestProcessor che si occuperà di catturarla e delegarne la gestione alla classe ExceptionHandler . <global-exceptions> <exception bundle="ApplicationResources" key="chiaveMessaggioErrore" path="/paginaErrore.jsp" type="org.apache.struts.util.ModuleException"/> </global-exceptions> L’unico limite nell’utilizzo di questa classe è di determinare un accoppiamento tra l’applicazione e la modalità di gestione delle eccezioni di Struts, rendendo più complesso il procedimento di sostituzione del framework adottato, lasciando inalterato lo strato applicativo sottostante. 5. Internazionalizzazione (I18N) Tipicamente, gli sviluppatori focalizzano la loro attenzione sulla realizzazione di un’applicazione per la risoluzione di un certo problema. Nella maggior parte dei casi, viene perso di vista un aspetto fondamentale che riguarda il pubblico a cui è diretto l’utilizzo dell’applicazione stessa. Soprattutto nel caso di Web application fruibili attraverso Internet, si pone il problema di permettere l’utilizzo di quest’ultima, a persone di paesi e lingue differenti. L’Internazionalizzazione (I18N) è un processo che prevede lo sviluppo di un software per poter supportare nel tempo più lingue e stati diversi , in modo tale che non ci sia bisogno di reingegnerizzare il sistema nel momento in cui sarà necessario fornire supporto per ulteriori di essi. 105 Capitolo III – Il framework Struts _________________________________________________________________ Le caratteristiche principali di un software che supporta l’Internazionalizzazione sono le seguenti : - le lingue devono essere supportate senza la necessità di apportare modifiche al codice; - i messaggi da visualizzare agli utenti sono contenuti in risorse esterne e completamente al di fuori del codice; Il framework Struts mette a disposizione tali caratteristiche utilizzando le principali classi Java che si occupano dell’internazionalizzazione : Locale e ResourceBundle. La classe Locale contiene le informazioni relative appunto al “locale” associato all’utente che utilizza l’applicazione. Con il termine “locale” si intende una regione nell’ambito della quale sono condivisi costumi, culture e lingue. Attraverso tale classe, è possibile risalire alle informazioni relative alla valuta, alla formattazione delle date e delle ore ed a tante altre informazioni specifiche. La classe ResourceBundle permette, invece, di definire la risorsa esterna all’interno della quale sono salvati i messaggi da visualizzare all’utente in una certa lingua. Attraverso l’utilizzo di questa classe, è possibile accedere ad essi direttamente da codice, mediante un meccanismo “key-message” (chiave-messaggio), in base al quale, specificando una chiave, si ricava il messaggio corrispondente associato. Generalmente, quando un utente accede ad una Web application realizzata con Struts, quest’ultimo memorizza all’interno della sessione utente, il locale definito dal browser che quest’ultimo sta adottando. Mediante questa informazione, è quindi possibile accedere al giusto Resource Bundle, da cui prelevare i messaggi da visualizzare all’utente nella sua lingua. Struts prevede la definizione di uno o più file .properties, aventi lo stesso nome ma un suffisso diverso, che specifica la lingua adottata nei messaggi contenuti nel file, attraverso le coppie “key-message”. 106 Capitolo III – Il framework Struts _________________________________________________________________ 6. JavaServer Pages Standard Tag Library (JSTL) Per la realizzazione della View, Struts mette a disposizione alcune librerie di tag, attraverso le quali, dalle pagine JSP, è possibile eseguire le seguenti operazioni: - visualizzare gli elementi costitutivi dei form e che caratterizzano completamente l’interfaccia utente; - accedere alle proprietà dei bean che caratterizzano l’applicazione; - eseguire delle operazioni logiche, in modo da permettere la costruzione di ciascuna pagina in base al verificarsi o meno di determinate condizioni; Attraverso queste librerie, è possibile eseguire tutte le operazioni necessarie, anche se manca uno strumento molto potente come l’Expression Language. Quest’ultimo, a differenza del framework JSF, non è supportato in forma nativa in Struts, per cui dalle pagine JSP non è possibile accedere a metodi e proprietà dei bean utilizzando le tipiche espressioni dell’EL, caratterizzate dalla “dot notation”. Per superare questo limite, è stata realizzata una versione “evoluta” delle librerie del tutto uguali a quelle di base, con l’unica differenza che i tag supportano l’Expression Language. Nella maggior parte dei casi, però, si preferisce utilizzare un particolare set di librerie di tag noto come JSTL (JavaServer Pages Standard Tag Library) che ovviamente supporta l’EL. Queste librerie sono complessivamente cinque, così definite : - Core : per l’accesso alle proprietà dei bean dell’applicazione, per gestire il flusso di controllo attraverso costrutti condizionali ed iterativi e per la gestione degli URL ; - Internationalization : per la gestione dell’internazionalizzazione (I18N), per la visualizzazione e formattazione dei messaggi; - XML : per la manipolazione di documenti XML direttamente da una pagina JSP; - SQL : per effettuare accessi ed operazioni a basi di dati; - Functions : fornisce una serie di funzioni di uso comune da poter applicare a variabili oppure proprietà dei bean; 107 Capitolo III – Il framework Struts _________________________________________________________________ Tipicamente, per la realizzazione di un’applicazione con Struts, sono necessarie soltanto le prime due che vanno a sostituire le librerie HTML, Bean e Logic. La libreria XML viene utilizzata solo nel caso in cui sia necessario gestire direttamente da una pagina JSP dei documenti XML, mentre la libreria Functions trova applicazione qualora si abbia bisogno di funzioni particolari da eseguire. Infine, la libreria SQL viene utilizzata solo nel caso di applicazioni di dimensioni estremamente ridotte, in quanto non è mai una buona soluzione, introdurre il codice per accedere alle basi di dati, all’interno della View. Nella maggior parte dei casi, si preferisce realizzare delle classi che si occupano esclusivamente dell’accesso ai database, in modo da separare nettamente queste operazioni dalla presentazione dei risultati. 7. Estensioni con i PlugIn Il framework Struts mette a disposizione un particolare meccanismo mediante il quale è possibile estenderne la struttura, ossia la definizione e l’uso del plug-in. Basti pensare che gli stessi Tiles e Validator sono dichiarati come tali all’interno del file di configurazione, in quanto non sono parte integrante dell’architettura di Struts. Ovviamente, lo sviluppatore ha la possibilità di realizzare una classe che implementi l’interfaccia PlugIn per poter disporre di un proprio plug-in con cui estendere le funzionalità dell’applicazione sviluppata. L’uso di un plug-in può essere particolarmente conveniente quando è necessario effettuare una serie di elaborazioni una sola volta, nella fase di start-up dell’applicazione. Infatti, il framework effettua il caricamento dei plug-in configurati nella fase di inizializzazione della Web application, così come la corrispondente eliminazione nel caso di shutdown oppure riavvio del Web Container. In entrambi i casi, vengono eseguiti i due corrispondenti metodi di cui deve essere sempre dotata una classe che rappresenta un plug-in : - initi() : viene eseguito all’avvio dell’applicazione e può contenere tutte le operazioni necessarie allo startup della Web application; - destroy() : viene invocato alla chiusura dell’applicazione per “distruggere” il plug-in; 108 Capitolo III – Il framework Struts _________________________________________________________________ public void init(ActionServlet actionServlet, ModuleConfig moduleConfig) throws ServletException { ... } public void destroy() { ... } Una volta effettuato l’overriding dei due metodi suddetti, basta dichiarare il plug-in all’interno del file struts-config.xml. <plug-in className="package.ClassePlugIn"/> 8. Validator framework Per poter eseguire la validazione dei dati immessi dall’utente all’interno dei form, il framework Struts prevede che la classe ActionForm, utilizzata per definire un formbean, abbia il metodo validate() che venga eseguito subito dopo l’invio del form stesso. Un approccio di questo tipo è , ovviamente, programmatico e talvolta può presentare una serie di limitazioni. In primo luogo, c’è da dire che la maggior parte dei form presenti in una Web application, hanno dei campi che possono contenere dei dati soggetti allo stesso tipo di validazione, per cui si è costretti a realizzare più metodi validate() praticamente uguali. Nel momento in cui c’è la necessità di effettuare una modifica, quest’ultima andrà eseguita in tutti i metodi realizzati e comporterà la ricompilazione del codice. Per questi motivi, è stato implementato un particolare framework che permette di gestire la validazione dei dati in maniera dichiarativa. Dato il suo notevole utilizzo, da parte degli sviluppatori, si è deciso di considerarlo un progetto Jakarta ed introdurlo in ogni distribuzione di Struts. Il framework Validator permette di trasferire la logica della validazione completamente fuori dalle classi ActionForm, configurando in maniera dichiarativa tutte le regole di validazione in un file XML. Esso fornisce una serie di funzioni standard per le validazioni più comuni ma è completamente estendibile, in quanto 109 Capitolo III – Il framework Struts _________________________________________________________________ permette allo sviluppatore di realizzare e configurare dei metodi di validazione personalizzati. La distribuzione del framework prevede una serie di classi ed in particolare due file XML, che ne permettono la configurazione in maniera semplice : - validation-rules.xml; - validation.xml; Il file validation-rules.xml contiene una serie di funzioni che permettono di eseguire le validazioni tipiche, come ad esempio : - valutare se l’input immesso dall’utente sia o meno di un certo tipo (Integer, Float, Double, Long,…); - valutare se un data immessa abbia un formato corretto; - valutare se l’input ricada entro un certo range oppure abbia una lunghezza che rispetto un limite minimo e massimo fissati; - valutare se l’utente abbia immesso un valore in un campo che sia obbligatorio; - valutare se un certo dato soddisfi un particolare pattern; - valutare se un valore che debba rappresentare una carta di credito oppure un indirizzo email, abbia un formato corretto; Ovviamente, a queste funzioni incluse nel framework ne possono essere aggiunte delle ulteriori, estendendo le classi principali del framework stesso. Il file validation.xml permette di configurare tutti i form della Web application, i cui campi debbano essere soggetti a validazione e, per ogni form, specificare per ciascun campo la funzione di validazione da applicare. Il tag <formset> viene utilizzato per raggruppare tutti i form suddetti, ciascuno dei quali viene definito mediante il tag <form>. Inoltre, all’interno di quest’ultimo sono previsti uno o più tag <field> che permettono di specificare i campi che costituiscono il form in questione. Per ciascuno di essi è possibile specificarne il nome, mediante l’attributo property, e la regola di validazione da applicare, mediante l’attributo depends. 110 Capitolo III – Il framework Struts _________________________________________________________________ <form name="nomeForm"> <field depends="required" property="proprieta1"/> ... <field depends="required,minlength" property="proprietaN"> </form> Generalmente, i form e le relative proprietà specificate all’interno di questo file, devono coincidere con le informazioni dei corrispondenti formbean che sono configurati all’intero del file struts-config.xml dell’applicazione. L’ultima considerazione da fare è legata al fatto che il framework Validator è perfettamente integrato con Struts ed il suo utilizzo prevede che esso venga dichiarato all’interno del file di configurazione come se fosse un plug-in, specificando i percorsi dei file XML che lo caratterizzano. <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/> </plug-in> 9. Tiles framework Tiles è un framework mediante il quale è possibile realizzare il Presentation Layer di una Web application, separando il layout dai contenuti (contents). Mentre il layout definisce la struttura di una pagina, con la caratteristica che più pagine possono avere il medesimo layout, il contents definisce il contenuto di ciascuna di esse e può essere diverso da pagina a pagina oppure eventualmente uguale in alcuni casi. E’ notevolmente importante poter separare l’uno dall’altro, in modo tale che apportando delle modifiche al primo non sussiste alcuna influenza sul secondo e viceversa. Il framework permette di costruire le pagine assemblando i cosiddetti “tiles”, il cui significato è banalmente “mattonelle” evidenziando che sono dei componenti riutilizzabili che possono essere disposti in una struttura predefinita. Nella maggior parte dei casi, ogni tile non è nient’altro che una pagina JSP che a sua volta può essere composta da altri tiles. E’ possibile distinguerne due tipologie : - tile Layout : tile che definisce la struttura di una pagina. Esso riceve da una pagina (tile non-Layout) il contenuto e lo dispone secondo tale struttura; 111 Capitolo III – Il framework Struts _________________________________________________________________ - tile non-Layout : tile che utilizza un layout (tile Layout) passando a quest’ultimo degli attributi che rappresentano il contenuto da disporre secondo una certa struttura; Generalmente, nell’ambito di una Web application, il tile Layout è unico e definisce la struttura di tutte le pagine, mentre ciascuna pagina è definita attraverso un proprio tile non-Layout. L’uso dei tiles può essere paragonato all’uso dei metodi in Java. Un metodo in Java è costituito da un body che ne definisce l’elaborazione e dai parametri di ingresso che specificano i dati su cui quest’ultima va effettuata. Un tile può essere considerato come un metodo, in quanto bisogna definirne la struttura e passargli dei parametri, detti attributi, che ne descrivano il contenuto. Ciascuno di essi è memorizzato nel context del tile stesso, attraverso la classe ComponentContext facente parte del framework. Infine, ogni attributo può essere una stringa oppure tipicamente una pagina JSP. Nella maggior parte dei casi, il layout utilizzato per ciascuna pagina è proposto in figura. Figura 11 - Layout Header/Footer/Menu/Body Esso prevede fondamentalmente quattro parti : header, footer, menu e body. Tipicamente, l’header ed il footer sono comuni a tutte le pagine, per cui il loro contenuto viene definito un’unica volta. Il vantaggio che ne scaturisce è dato dal fatto che se deve essere apportata una modifica ad uno di essi, quest’ultima va eseguita 112 Capitolo III – Il framework Struts _________________________________________________________________ un’unica volta e non per tutte le pagine della Web application. Il menu, invece, può essere lo stesso nell’ambito di tutta l’applicazione e quindi avrà il medesimo vantaggio di header e footer oppure può variare in relazione alla pagine visitate. Infine, il body è tipicamente diverso da pagina a pagina. Per quanto riguarda la relazione con Struts, Tiles è perfettamente integrato con quest’ultimo, in quanto contenuto in ciascuna distribuzione, e viene dichiarato all’interno del file di configurazione in termini di plug-in. <plug-in className="org.apache.struts.tiles.TilesPlugin"> <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/> <set-property property="moduleAware" value="true"/> </plug-in> 9.1 Definitions I tiles possono essere descritti attraverso le cosiddette definitions, dichiarate in un file XML e che permettono : - una dichiarazione centralizzata di una pagina; - se si dispone di più tiles non-Layout con un contenuto uguale (es. le pagine hanno il medesimo header e footer), al posto di definire tale contenuto pagina per pagina, lo si definisce un’unica volta rendolo comune a tutte le pagine; - supporto alla derivazione, descrivendo una nuova definition che ne estenda una preesistente, ereditandone le caratteristiche ed aggiungendone delle altre; Generalmente, il file che contiene le definitions è noto come tiles-defs.xml e ciascuna di esse è dichiarata attraverso il tag <definition> che mediante i suoi attributi permette di specificare le seguenti informazioni : - nome della definition per referenziarla all’interno di un tile che ne farà uso (name); - percorso del tile Layout che descrive la struttura associata alla definition (path); 113 Capitolo III – Il framework Struts _________________________________________________________________ - definition da cui eseguire la derivazione (extends); Per specificare gli attributi della definition, è possibile utilizzare uno o più tag <put>, ciascuna dei quali prevede le seguenti informazioni : - nome dell’attributo; - valore dell’attributo che può essere una stringa, una pagina JSP oppure un’altra definition se si prevede di utilizzare la composizione di più definitions; <tiles-definitions> <definition name="page.template" path="/paginaLayout.jsp"/> <definition name="page.header" path="/header.jsp"/> <definition name="page.footer" path="/footer.jsp"/> <definition name="page.menu" path="/menu.jsp"/> </tiles-definitions> 9.2 Costruzione di una pagina La costruzione di una pagina JSP secondo un certo layout e con uno specifico contenuto è realizzata attraverso l’utilizzo di particolari tag della libreria Struts Tiles. Il tag che permette di inserire un contenuto oppure utilizzare una definition all’interno di una pagina JSP è <tiles:insert>, il quale prevede le seguenti informazioni : - percorso della pagina da inserire (page); - nome dell’attributo (attribute); - nome della definition da adottare all’interno di un tile non-Layout (definition); Ovviamente, la definition utilizzata all’interno di ciscuna pagina avrà dei contenuti comuni fra le pagine ma queste ultime potrebbero anche avere alcuni contenuti differenti. Per inserire un contenuto nel layout, specificato da una definition, è possibile utilizzare il tag <tiles:put>, il quale descrive le seguenti informazioni : - nome dell’attributo (name); 114 Capitolo III – Il framework Struts _________________________________________________________________ - valore dell’attributo che può essere una stringa oppure una pagina JSP (value); <tiles:insert definition="page.template"> <tiles:put name="title" value="Titolo"/> <tiles:put name="body" value="/BodyContext.jsp" type="page"/> </tiles:insert> Attraverso questo meccanismo, le pagine possono avere contenuti comuni che non devono ulteriormente specificare in quanto definiti all’interno della definition utilizzata. Nel caso in cui alcuni contenuti debbano essere differenti, basta utilizzare il tag <tiles:put> mediante il quale il contenuto specificato va a sostituire quello di default. 10. Sicurezza La sicurezza di un’applicazione realizzata con il framework Struts rappresenta un aspetto completamente indipendente da quest’ultimo e può essere gestita mediante le funzionalità offerte dal Web Container. Parlare di sicurezza attraverso la rete significa soprattutto garantire la trasmissione delle informazioni e dei dati in modalità crittografata, in modo tale da essere inaccessibili dall’esterno. Il meccanismo principale messo a disposizione si basa sul protocollo SSL (Secure Socket Layer), sulla base del quale è stata creata in passato una nuova versione del protocollo HTTP, ossia l’HTTPS. Mediante quest’ultimo, infatti, le informazioni sono trasmesse dal client al server e viceversa in modalità crittografata, anche sulla base dell’utilizzo di un regolare certificato digitale. Per poter fare in modo che le pagine di un’applicazione implementata con Struts, utilizzino tale protocollo, è necessario in primo luogo abilitarlo all’interno del Web Container e successivamente modificare il Deployment Descriptor web.xml dell’applicazione stessa. All’interno di quest’ultimo è possibile dichiarare dei vincoli di sicurezza (tag <security-constraint>) e le pagine che ne sono soggette (tag <web-resource-collection>), oltre al tipo di trasporto da utilizzare (tag <transport-guarantee>) che può essere di tre tipi : - NONE : utilizza HTTP; 115 Capitolo III – Il framework Struts _________________________________________________________________ - INTEGRAL , CONFIDENTIAL : utilizza HTTPS; <security-constraint> <display-name>SSL Constraint</display-name> <web-resource-collection> <web-resource-name> Automatic SLL Forwarding </web-resource-name> <url-pattern>[pagina.jsp]</url-pattern> <http-method>GET</http-method> <http-method>PUT</http-method> <http-method>POST</http-method> <http-method>DELETE</http-method> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> Quanto detto mette in evidenza che la sicurezza di un’applicazione realizzata con Struts, è gestita allo stesso modo di una qualsiasi applicazione Web in Java e quindi completamente indipendente dal framework. 11. Configurazione dell’applicazione Il framework Struts utilizza uno o più file di configurazione per poter creare e caricare in fase di start-up dell’applicazione, tutte le risorse ed i componenti necessari a quest’ultima. Tali file permettono di specificare in maniera dichiarativa il comportamento degli elementi dell’applicazione, evitando che tali specifiche siano definite all’interno del codice. Questo permette agli sviluppatori di realizzare delle estensioni e di utilizzarle nella Web application, garantendo che il framework sia capace di sfruttarle in maniera dinamica. I file di configurazione si basano sul linguaggio XML e possono essere anche più di uno, nel momento in cui l’applicazione è realizzata con moduli indipendenti. Ciascuno di essi ha le proprie informazioni di configurazione, inclusi i Resource Bundles, ed è completamente indipendente dagli altri. Tutto ciò favorisce lo sviluppo di un’applicazione di grandi dimensioni, anche da team di lavoro distinti. Con la versione 1.1 del framework Struts, è stato introdotto il package config, all’interno del quale ci sono una serie di classi che permettono di memorizzare le informazioni di configurazione, nel momento in cui queste ultime vengono lette dal 116 Capitolo III – Il framework Struts _________________________________________________________________ file XML. In questo modo, si ha a disposizione una visione di queste informazioni in termini di classi, residenti nella memoria e quindi accessibili dallo sviluppatore direttamente da codice. Ciascuna classe del package contiene le informazioni che riguardano una parte specifica del file di configurazione. Dopo che è stato eseguito il parsing e la validazione del file di configurazione, il framework istanzia ed alloca in memoria, gli oggetti contenenti le informazioni ad essi pertinenti. Figura 12 - Class Diagram Configurazione La classe centrale del package è l’ ApplicationConfig, la quale è legata a tutte le altre classi contenenti le informazioni di configurazione. Attraverso l’istanza di tale classe, è possibile accadere a tutte le altre e quindi alle informazioni in esse memorizzate. Se l’applicazione è sviluppata in moduli, ciascun modulo ha a sua disposizione una corrispondente istanza di tale classe. 11.1 DataSource Generalmente, una Web application prevede l’utilizzo di un database all’interno del quale sono memorizzate le informazioni in maniera permanente. 117 Capitolo III – Il framework Struts _________________________________________________________________ L’utilizzo di una base di dati, oppure più generalmente di un datasource, può essere specificato all’interno del file di configurazione. Per questo scopo, è messo a disposizione il tag <data-source>, che va utilizzato più volte in relazione al numero di database di cui dispone l’applicazione, distinguendo un datasource dall’altro attraverso la specifica di una chiave (key). Inoltre, è possibile definire una serie di proprietà caratteristiche del datasource, utilizzando al suo interno uno o più tag <setproperty>. Le informazioni tipicamente configurate sono le seguenti : - una descrizione del datasource; - la classe che implementa il driver per l’accesso alla base di dati. In generale, sono utilizzati i driver JDBC, ma in molti casi si può fare anche uso di driver specifici per il connection-pooling; - lo username e la password per accedere alla base di dati; - l’ URL in cui si trova il database; Tutte queste informazioni sono memorizzate, in fase di start-up dell’applicazione, nella classe DataSourceConfig, mediante la quale è possibile accedere alle proprietà del datasource, ed eventualmente modificarle, direttamente da codice. <data-sources> <data-source key="database"> <set-property property="driverClassName" value="com.mysql.jdbc.Driver"/> <set-property property="url" value="jdbc:mysql://localhost/database"/> <set-property property="username" value="admin"/> <set-property property="password" value="sara"/> </data-source> </data-sources> 11.2 FormBean Ciascun formbean realizzato come estensione della ActionForm ed utilizzato all’interno dell’applicazione, deve essere specificato nel file di configurazione mediante l’uso del tag <form-bean>. 118 Capitolo III – Il framework Struts _________________________________________________________________ Quest’ultimo permette di specificare le seguenti informazioni : - il nome del formbean mediante il quale si farà riferimento ad esso nel resto del file (name). Ad esempio, quando si definisce una action che utilizza i dati di un certo formbean, va specificato il nome di quest’ultimo; - la classe che implementa il formbean (type). Tipicamente, questa può essere la classe ActionForm oppure DynaActionForm o eventualmente una delle classi che derivano da esse. In alcuni casi, è invece una classe realizzata dallo sviluppatore, che comunque estende una delle classi suddette; <form-bean name="nomeFormBean" type="package.ClasseActionForm"/> E’ ovvio che, nel caso in cui la classe che implementa il formbean sia stata realizzata dallo sviluppatore, le proprietà del formbean stesso sono esattamente le proprietà della classe, che non devono essere ulteriormente specificate nel file di configurazione. Nel caso in cui, si preferisce adottare ad esempio un formbean dinamico, è necessario specificare le proprietà del formbean all’interno del file strutsconfig.xml, non essendoci una classe realizzata ad hoc. Per questo scopo, il tag <form-bean> può contenere uno o più tag <form-property>, ciascuno dei quali permette di definire una proprietà del formbean, specificandone le seguenti informazioni : - il nome della proprietà (name); - la classe (oppure il tipo di dato primitivo) della proprietà (type); - l’eventuale valore iniziale, quando il formbean è vuoto (initial); <form-bean name="nomeFormBeanDinamico" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="porprieta1" type="ClasseProprieta1"/> ... <form-property name="proprietaN" type="ClasseProprietaN"/> </form-bean> Tutte queste informazioni sono memorizzate all’interno delle classi FormBeanConfig e FormPropertyConfig, che contengono rispettivamente tutto ciò che riguarda in generale il formbean e le relative proprietà. 119 Capitolo III – Il framework Struts _________________________________________________________________ 11.3 Global Exceptions Il framework Struts mette a disposizione la possibilità di gestione delle eccezioni che possono essere sollevate all’interno delle action. Ovviamente, è possibile definire, per ciascuna action, le eventuali eccezioni che essa può sollevare, ma in moltissimi casi, esistono delle eccezioni che possono essere sollevate da più action diverse. In questi casi, la soluzione migliore non è certamente quella che prevede di specificare la stessa eccezione per ogni action, ma bensì di definire una eccezione come “globale”. In questo modo, quando una action solleva un’eccezione, l’ ExceptionHandler valuta in primo luogo se essa è specifica della action che l’ha sollevata oppure è di tipo “globale” e va gestita in un certo qual modo. Ciascuna eccezione “globale” viene specificata nel file di configurazione, all’interno del tag <global-exceptions>, facendo uso del tag <exception>, il quale permette di definire le seguenti informazioni : - la chiave relativa al Resource Bundle e corrispondente al messaggio da visualizzare, quando l’eccezione viene sollevata (key); - il percorso della pagina alla quale l’utente verrà redirezionato (path); - la classe “handler” che si occuperà di gestire l’eccezione. Per default, essa è la classe ExceptionHandler, ma ovviamente lo sviluppatore può realizzarne una propria estendendo quest’ultima; - la classe che rappresenta il tipo di eccezione (type); La classe che memorizza tutte queste informazioni è la ExceptionConfig. <global-exceptions> <exception bundle="ApplicationResources" key="chiaveMessaggioErrore" path="/paginaErrore.jsp" type="org.apache.struts.util.ModuleException"/> </global-exceptions> 120 Capitolo III – Il framework Struts _________________________________________________________________ 11.4 Global Forwards Quando una action, all’interno del metodo execute(), termina le operazioni relative al proprio scopo, essa restituisce un oggetto ActionForward con il quale si definisce la risorsa verso la quale proseguire la navigazione. Per ciascuna action, all’interno del file di configurazione, vengono specificati i “forward” ad essa associati. In alcuni casi, può essere utile fare uso dei forward “globali”, i quali non sono specifici di una action, ma possono essere referenziati ad una qualsiasi action all’interno dell’applicazione. Ciascun forward “globale” viene specificato nel file di configurazione, all’interno del tag <global-forwards>, facendo uso del tag <forward>, il quale permette di definire le seguenti informazioni : - il nome del forward per referenziarlo all’interno di una Action (name); - il percorso della risorsa fisica a cui fa riferimento il forward “logico” (path); Queste informazioni sono memorizzate nella classe ForwardConfig. <global-forwards> <forward name="nomeForward" path="pagina.jsp"/> ... </global-forwards> 11.5 Action Mapping Il contenuto principale del file di configurazione di Struts è il mapping delle action, attraverso il quale è possibile specificare tutte le action utilizzate all’interno del framework, associarle ad un percorso logico per la loro invocazione e specificarne i forward per proseguire la navigazione da ciascuna di esse. Per definire ciascuna action, viene utilizzato il tag <action>, mediante il quale sono descritte le seguenti informazioni : - il percorso (URI) della action per poter essere invocata da un qualsiasi punto dell’applicazione (path); 121 Capitolo III – Il framework Struts _________________________________________________________________ - la classe che implementa la action, tipicamente realizzata dallo sviluppatore, estendendo la classe di base Action oppure una delle sue derivate (type); - il nome del formbean, i cui dati saranno oggetto dell’elaborazione della action (name); - la visibilità del formbean (scope), distinguendo fra request e session; - la pagina alla quale ritornare il controllo, qualora si verificassero errori di validazione dei dati nel formbean (input); - un eventuale parametro che permette di specificare quale metodo della action eseguire nel caso in cui si tratti di una DispatchAction; Inoltre, attraverso l’attributo validate, è possibile specificare se, prima di invocare la action, debba essere eseguita o meno la validazione dei dati contenuti nel formbean associato. Infine, al termine dell’esecuzione del metodo execute(), la action restituisce l’oggetto ActionForward che specifica la risorsa verso la quale proseguire la navigazione. Attraverso uno o più tag <forward>, inclusi nel tag <action>, è possibile specificare tutti i “forward” che può restituire la action stessa. Le informazioni principali sono tipicamente le seguenti : - il nome del forward per referenziarlo all’interno di una action (name); - il percorso della risorsa fisica a cui fa riferimento il forward “logico” (path); Inoltre, è possibile specificare anche le eccezioni che possono essere sollevate da ciascuna action, facendo uso del tag <exception> così come vengono definite le eccezioni “globali”. Tutte le informazioni descritte vengono memorizzate nella classe ActionConfig, in stretta relazione con le classi ForwardConfig ed ExceptionConfig. <action input="pagina.jsp" name="nomeFormBean" parameter="dispatch" path="/nomeAction" scope="request" type="package.ClasseAction"> <forward name="nomeForward1" path="pagina1.jsp"/> ... <forward name="nomeForwardN" path="paginaN.jsp"/> </action> 122 Capitolo III – Il framework Struts _________________________________________________________________ 11.6 Controller Il framework, nell’ambito della gestione di tutte le richieste che arrivano alla Web application, utilizza le versioni di default per le classi ActionServlet e RequestProcessor, che realizzano il Controller. Nel momento in cui lo sviluppatore decide di realizzare un Controller personalizzato, non fa altro che estendere la classe RequestProcessor e definire al suo interno le elaborazioni da eseguire. Nel file di configurazione, va specificato quale deve essere il Controller da adottare, facendo uso del tag <controller>, mediante il quale, tra le altre informazioni, va specificata in particolare la seguente : - la classe che implementa il Controller, la quale può essere quella di default RequestProcessor oppure una classe che derivi da quest’ultima (processorClass); Le informazioni riguardanti il Controller sono memorizzate nella classe ControllerConfig. <controller processorClass="controller.CustomRequestProcessor"/> 11.7 Message resources Per garantire l’internazionalizzazione, il framework Struts mette a disposizione la possibilità di realizzare una serie di file, all’interno di ciascuno dei quali salvare i messaggi da visualizzare all’utente, in una lingua specifica. Tali file sono anche noti come Resource Bundles o Message Resources e vanno specificati nel file di configurazione. In realtà, non è necessario definire l’elenco di tutti questi file, ma unicamente il file associato al linguaggio di default, poiché tutti gli altri avranno esattamente lo stesso nome, con in più un suffisso che ne distingue la lingua utilizzata. Per questo scopo, viene adottato il tag <message-resources>, nel quale è possibile specificare il nome del Resource Bundle, mediante l’attributo parameter. Tali informazioni sono memorizzate nella classe MessageResourcesConfig. <message-resources parameter="ApplicationResources"/> 123 Capitolo III – Il framework Struts _________________________________________________________________ 11.8 Plug-In Dalla versione 1.1 del framework, è stato introdotta la possibilità di utilizzare dei plug-in, mediante i quali estendere le potenzialità di quest’ultimo. I due esempi tipici sono il Validator Plug-In, per la validazione dei dati, ed il framework Tiles, per la definizione del layout dell’applicazione. Lo sviluppatore ha comunque la possibilità di realizzare una classe che estenda l’interfaccia PlugIn del framework, per implementare un proprio plug-in che venga eseguito in fase di start-up dell’applicazione. E’ ovvio che ciascun plug-in utilizzato va specificato nel file di configurazione, facendo uso del tag <plug-in>, nel quale, tra le altre informazioni, va segnalata principalmente la seguente : - la classe che estende l’interfaccia PlugIn ed implementa il plug-in (className); Tutte le informazioni relative ad un plug-in sono memorizzate nella classe PlugInConfig. <plug-in className="package.ClassePlugIn"/> 124 Capitolo IV – I framework a confronto _________________________________________________________________ Capitolo IV – I framework a confronto 1. Introduzione Per molti anni, Struts è stato il framework più popolare ed ampiamente utilizzato per la realizzazione di Web application mediante il linguaggio Java. In tempi recenti, è stato introdotto un nuovo framework, JavaServer Faces, che è stato definito in termini di standard ed ha posto la questione di dover scegliere quale delle due tecnologie adottare ed in quali condizioni un’applicazione realizzata con Struts dovesse utilizzare le nuove potenzialità di JSF. Le principali caratteristiche di Struts possono essere così riassunte : - un’architettura basata sul pattern di sviluppo MVC (Model – View – Controller); - capacità di gestione dei form (form management) mediante i cosiddetti formbean che rappresentano lo stato dei dati di input sul server, ai quali va aggiunto un framework per la validazione lato client e lato server; - presenza del framework Tiles per la definizione del layout delle pagine mediante dei templates facilmente modificabili, che riducono i tempi di manutenzione grafica dell’applicazione; - librerie di tag da utilizzare nelle pagine JSP per la definizione della View e che lavorano in stretta collaborazione con la parte di form-management e di controllo; A tali caratteristiche, si aggiunge l’elevato livello di maturità che caratterizza il framework, in quanto è stato realizzato nella sua prima versione nell’anno 2000, diventando uno standard de facto per lo sviluppo di Web application su piattaforma J2EE (Java2 Enterprise Edition). Un limite può essere dettato dal fatto che Struts rappresenti un’implementazione e quindi sia unico, ma comunque migliorabile ed estendibile. 125 Capitolo IV – I framework a confronto _________________________________________________________________ Dal lato opposto si pone il framework JavaServer Faces, le cui caratteristiche peculiari sono le seguenti : - architettura basata sul pattern MVC, ma focalizzata in particolar modo alla View; - componenti riutilizzabili ed estendibili per la realizzazione dell’interfaccia utente (UI); - modello di gestione degli eventi e dei corrispondenti handlers, basato su JavaBeans; - utilizzo dell’EL (Expression Language) per realizzare il value-binding e method-binding, mediante il quale è possibile legare le proprietà dei componenti della UI, rappresentati con tag nelle pagine JSP, direttamente con la business-logic, senza che tali componenti abbiano alcuna conoscenza della struttura delle classi di ques’ultima; - ciclo di vita di una richiesta ben definito attraverso una serie di fasi; - navigazione delle pagine basata sulla pagina attuale e le possibili destinazioni a partire da essa; - possibilità di gestione dei backing beans (managed beans) costituenti la business-logic che possono essere istanziati in un qualsiasi momento “on demand”; La prima release di questo framework è stata definita nel Marzo del 2004, il che evidenzia un livello di maturità ancora basso rispetto a Struts. D’altro canto, JavaServer Faces rappresenta uno standard del quale ne esistono due implementazioni differenti : JSF RI (Reference Implementation) della Sun Microsystems e MyFaces di Apache. Inoltre, il creatore di Struts, Craig McClanahan, ha contribuito alla creazione di JSF portando con se un enorme bagaglio di esperienza nella realizzazione di framework per lo sviluppo Web. Nella piattaforma J2EE 5.0 sarà prevista in forma nativa, un’ implementazione della tecnologia JavaServer Faces. In riferimento al pattern MVC, su cui si basano entrambi i framework, esso è anche noto come Front Controller, in quanto tutto le richieste che arrivano alla Web application vengono gestite e distribuite mediante un Controller 126 Capitolo IV – I framework a confronto _________________________________________________________________ unico dell’applicazione. A questo punto, prendendo in considerazione le caratteristiche dei due framework, si possono porre le seguenti domande : - quale framework adottare per lo sviluppo di una nuova Web application ? - disponendo di un’applicazione realizzata in Struts, conviene utilizzare i componenti aggiuntivi di JSF, svilupparla dall’inizio con ques’ultimo oppure eseguire una migrazione da un framework all’altro ? Per rispondere a queste domande, si rende necessario un confronto approfondito tra queste due tecnologie, nonché bisogna tenere conto delle caratteristiche e delle dimensioni dell’applicazione da realizzare. 2. Ciclo di vita di una richiesta Una delle principali differenze tra i due framework risiede nel ciclo di vita di una richiesta (request life-cycle), ossia cosa accade dal momento in cui arriva una richiesta (request) all’applicazione fino alla produzione della corrispondente risposta (response). In Struts, il ciclo di vita è relativamente semplice e può essere schematizzato nei seguenti passi principali : - il Controller riceve una richiesta in ingresso; - i parametri della richiesta vengono trasferiti nel corrispondente formbean; - viene determinata la action, che rappresenta un bridge tra Controller e Model, che dovrà gestire tale richiesta; - il formbean viene passato alla action individuata e ne viene avviata l’esecuzione; - all’interno della action vengono utilizzati gli oggetti della business-logic; - al termine dell’elaborazione, la action restituisce un “forward” al Controller che, sulla base di quest’ultimo, determina la View che visualizzerà la risposta al client; 127 Capitolo IV – I framework a confronto _________________________________________________________________ 1. Richiesta Client 7. Determinazione della View CONTROLLER 2. Trasferimento dati della richiesta VIEW 3. Determinazione della action action formbean 6. Restituzione “forward” al Controller 4. Passaggio del formbean alla action 5. Utilizzo oggetti della Business-Logic MODEL 8. Risposta Client Figura 1 - Struts : ciclo di vita di una richiesta JavaServer Faces ha una gestione molto più complicata, caratterizzata anche dal fatto che è possibile distinguere due tipi differenti di richieste : initial request e postback. Inoltre, ciascuna richiesta viene manipolata attraverso sei fasi differenti, i cui passi principali sono i seguenti : - il Controller riceve una richiesta in ingresso; - viene ricostruito lo stato iniziale della View, qualora si tratti di una initial request, oppure uno stato precedente nel caso di un postback; - i valori dei parametri della richiesta vengono trasferiti nelle proprietà corrispondenti dei componenti della UI; 128 Capitolo IV – I framework a confronto _________________________________________________________________ - viene eseguita la validazione dei dati; - assumendo che i dati siano validi, essi vengono trasferiti dai componenti alle proprietà dei backing beans ad essi associati; - viene eseguita la gestione di eventuali eventi sui componenti della UI e le elaborazioni della business-logic, specificate nei backing beans; - infine, viene eseguita la fase di rendering per la produzione della risposta; 1. Richiesta Client 8. Risposta Client CONTROLLER Restore View Apply Request Values 2. Ripristino stato View VIEW 3. Trasferimento dati della richiesta Componenti UI Process Validations Update Model Values Invoke Application 4. Validazione dati 5. Trasferimento dati dai componenti UI ai backing beans 6. Metodi Business-Logic MODEL Backing Beans Render Response 7. Rendering della View Figura 2 - JSF : Ciclo di vita di una richiesta 129 Capitolo IV – I framework a confronto _________________________________________________________________ 3. Controller Tier Entrambi i framework prevedono un Controller realizzato attraverso una Servlet, in esecuzione nel Web Container, la quale ha il compito di acquisire le richieste provenienti dai client e determinare per ciascuna di esse il corrispondente handler, facente parte del Model, che dovrà gestirla. Infine, sulla base dei risultati prodotti, la stessa Servlet si fa carico di determinare la View che rappresenterà la risposta fornita al client. L’implementazione del Controller è ovviamente differente e prevede le seguenti classi : - Struts : la classe ActionServlet che viene coadiuvata da una o più istanze dalla classe RequestProcessor; - JSF : la classe FacesServlet; In Struts, la presenza di due classi rappresenta una prerogativa necessaria per la realizzazione di una Web application in più moduli separati. Infatti, l’istanza della ActionServlet è unica mentre, per ciascun modulo dell’applicazione , è prevista un’istanza della classe RequestProcessor. Quando una richiesta arriva al Web Container, la ActionServlet la smista verso l’istanza della RequestProcessor che è associata al modulo a cui la richiesta stessa è destinata. Detto ciò, tutti i compiti tipici del Controller vengono svolti dalla classe RequestProcessor. Questa differenza può rappresentare un vantaggio per il framework Struts, per quanto riguarda l’estendibilità del Controller. Infatti, lo sviluppatore deve semplicemente realizzare una classe che estenda la RequestProcessor ed esegua l’overriding del metodo processPreprocess(), qualora voglia eseguire delle operazioni preliminari alla gestione di una richiesta. Viene quindi messo a disposizione un metodo, tipicamente non utilizzato dal framework, per estendere il Controller. 130 Capitolo IV – I framework a confronto _________________________________________________________________ Richiesta Client ActionServlet RequestProcessor Punto di estensione del Controller MODEL Figura 3 - Struts : Controller Tier In JSF, l’estensione è più complicata, in quanto la gestione della richiesta avviene in più fasi separate. Per questo motivo, è possibile creare un oggetto PhaseListener, relativo ad una specifica fase ed associarlo all’istanza della classe Lifecycle, che gestisce il ciclo di vita di una richiesta. In corrispondenza di tale oggetto, è possibile specificare le elaborazioni da eseguire prima e dopo la fase associata, eseguendo l’overriding dei metodi afterPhase() e beforePhase(). Rispetto a Struts, tale approccio ha una complessità nettamente superiore, dettata soprattutto dalla notevole differenza che c’è nel ciclo di vita di una richiesta. Per questo motivo, in Struts basta estendere la sola classe RequestProcessor ed eseguire l’overriding del metodo processPreprocess() prima che una richiesta venga gestita, mentre in JSF, considerando la presenza di più fasi, è possibile intervenire con una maggiore granularità in corrispondenza di ogni fase, addirittura definendo delle operazioni specifiche da eseguire prima e dopo ciascuna di esse. 131 Capitolo IV – I framework a confronto _________________________________________________________________ Richiesta Client FacesServlet Lifecycle Restore View Apply Request Values Punti di estensione del Controller Process Validation Update Model Values Invoke Application Render Response Figura 4 - JSF : Controller Tier 132 Capitolo IV – I framework a confronto _________________________________________________________________ 4. Interfaccia Utente (UI) La realizzazione dell’interfaccia utente di una Web application è uno degli aspetti in cui il framework JavaServer Faces può considerarsi nettamente superiore rispetto a Struts. Infatti, JSF introduce il concetto di componente così come nelle applicazioni desktop è previsto il concetto di controllo, ad esempio con l’utilizzo di Swing. Ciascun componente rappresenta un elemento costitutivo dell’interfaccia utente e può avere forme diverse partendo dai componenti più semplici quali un campo di testo, un radio button, una checkbox, una drop-down list, un bottone, un link fino a considerare componenti notevolmente complessi che sono realizzati estendendo oppure collegando tra loro i precedenti. Ovviamente, anche Struts permette di realizzare un’interfaccia utente caratterizzata da elementi di questo tipo ma ciò che differenzia i due framework è la diversa implementazione e gestione degli stessi. Stato Valori ed Eventi JavaBean Class Component Validators Converters Renderers Tag XML-like Tag Handler Event Listeners Value & Method Binding Backing Bean Figura 5 - JSF : UI Components In JSF, i componenti della UI sono definiti attraverso una gerarchia di classi che ne determinano il comportamento, le funzionalità, la gestione degli eventi e lo stato, 133 Capitolo IV – I framework a confronto _________________________________________________________________ concetto non presente in Struts, che viene salvato tra una richiesta e l’altra sul server oppure eventualmente sul client. I componenti che costituiscono una pagina JSF sono organizzati secondo una struttura ad albero (view) a partire da un nodo radice (root). Essi sono realizzati attraverso dei JavaBeans che hanno delle proprietà, che rappresentano i propri attributi, dei metodi, che ne definiscono le funzionalità ed infine dei listeners per la gestione dei corrispondenti eventi. Inoltre, la loro visualizzazione può essere eseguita in maniera autonoma oppure delegata ai renderers. Il vantaggio principale di questa caratteristica è dato dal fatto che è possibile definire una sola volta il comportamento di un componente ma associandogli differenti renderers, per la visualizzazione su dispositivi client che utilizzano linguaggi di markup diversi. Ciò permette alla tecnologia JSF di supportare lo sviluppo di Web application, utilizzabili con device differenti, quali personal computer, palmari e cellulari, partendo da componenti comuni ma sfruttando visualizzazioni diversificate. Renderer HTML JavaBean Class Component Renderer WML Figura 6 - JSF : Rendering per device differenti Nel caso delle semplici pagine JSP, ogni componente viene introdotto mediante l’utilizzo di un tag corrispondente, nel quale è possibili fissare gli attributi del componente stesso. L’implementazione JSF – RI fornisce le classi di base dei componenti ed in un più un renderer kit e la libreria di tag associata, per la visualizzazione degli stessi, con client HTML. Nulla vieta di realizzare un renderer kit ed i corrispondenti tag che permettano la visualizzazione dei componenti con un 134 Capitolo IV – I framework a confronto _________________________________________________________________ linguaggio di markup diverso, quale XML oppure WML. Struts, invece, mette a disposizione una libreria di tag per i client HTML, ma non fornisce il supporto di indipendenza dai client, nel senso che non è possibile permette la visualizzazione dell’applicazione, da un dispositivo diverso da un browser web. Tag XML-like Tag Handler FormBean Figura 7 - Struts : Elementi UI Infine, JSF fornisce allo sviluppatore la possibilità di estendere le classi che implementano i componenti, per realizzarne di propri e personalizzati, caratteristica assolutamente non presente in Struts. Ad esempio, se si vuole realizzare un form costituito da più campi di input, che magari dovrà essere utilizzato in più pagine, con JSF si può realizzare un unico componente e quindi un unico tag da immettere nelle pagine JSP. La logica ed il comportamento, interni al componente, permetteranno di trattare i dati come un unico input. Con Struts, sarà necessario introdurre i controlli separatamente nelle pagine ed utilizzare più tag, uno per ogni campo di input ed inoltre, si avrà l’onere di raggruppare i dati ottenuti. Un caso tipico può essere quello relativo ad un componente che permetta all’utente di selezionare una data, ad esempio quella di nascita. Con JSF, si realizza un unico Custom Component (DateSelectComponent) ed il relativo Tag Handler, per disporre di un unico tag mediante il quale inserire l’elemento all’interno della pagina. Quest’ultimo è costituito da tre drop-down list, relative al giorno, al mese e all’anno, che sono trattate come un unico controllo della UI. La data selezionata viene gestita automaticamente dal componente che produce un oggetto java.util.Date, il quale viene immediatamente memorizzato in una corrispondente proprietà di un backing bean, mediante il value-binding. Con Struts, è necessario realizzare tre drop-down list separate i cui valori sono copiati in tre proprietà distinte di un formbean. Sarà compito della action eseguita, prelevare questi valori e comporli in modo da generare un unico oggetto java.util.Date da passare alla business-logic. 135 Capitolo IV – I framework a confronto _________________________________________________________________ DateSelect Component Class java.util.Date Object giorno mese anno FormBean Backing Bean java.util.Date Object JSF Business Object Action Struts Struts Figura 8 - JSF e Struts : esempio di componente composto Quanto detto, evidenzia il fatto che JSF sia nettamente orientato alla realizzazione della View di una Web application, permettendo una flessibilità ed una espandibilità nettamente superiori a Struts. 5. Events e Listeners Un aspetto innovativo di JSF rispetto a Struts è l’introduzione del concetto di evento associato ad un componente. Tale concetto è da sempre presente nelle applicazioni desktop, in cui su di un controllo dell’interfaccia utente può scatenarsi 136 Capitolo IV – I framework a confronto _________________________________________________________________ un evento, come la pressione di un bottone oppure il cambiamento del contenuto di un campo di input. L’applicazione prevede una serie di listeners che intercettano gli eventi e ne eseguono la gestione, effettuando delle elaborazioni. Tale approccio è da sempre noto come “event-driven”, ossia pilotato da eventi, nel senso che ogni computazione eseguita dal software è avviata al verificarsi di un evento su un controllo dell’interfaccia utente. In Struts tale concetto non esiste, in quanto l’unico evento che viene considerato tale è la semplice richiesta HTTP, per cui l’invio dei dati di un form, la pressione di un bottone, il click su un collegamento ipertestuale, vengono trattati come semplici richieste HTTP. Con JSF, il modello “event-driven” è applicato anche nelle Web application, per cui alla normale gestione delle richieste HTTP, si è aggiunta la particolare gestione degli eventi. Ciò vuol dire che ogni tipo di azione che l’utente esegue attraverso l’interfaccia può essere trattata come una normale richiesta oppure gestita attraverso i listeners associati agli eventi scatenati. Tali listeners possono essere realizzati attraverso dei metodi dei backing beans, la cui tipica applicazione è la realizzazione della navigazione tra le pagine, oppure mediante delle classi specifiche, che implementano delle particolari interfacce del framework. Il vantaggio sostanziale degli eventi è dettato dal fatto che un evento, scatenato su un certo componente, può essere gestito anche in modalità “immediata”, senza che la richiesta HTTP sottostante segua tutto il ciclo di vita previsto da JSF. Con JavaServer Faces, la navigazione è completamente gestita attraverso il meccanismo degli eventi di tipo “action”, ossia riferiti alla pressione di un bottone oppure il click su di un link. Infatti, nel momento in cui si vogliono inviare i dati di un form oppure si vuole passare ad una nuova pagina attraverso un collegamento ipertestuale, viene generato un evento di questo tipo che prevede l’esecuzione di un metodo di un backing bean associato al componente, che eseguirà delle elaborazioni e restituirà l’outcome per il proseguimento della navigazione. Questo evento può essere gestito anche creando una classe che implementa l’interfaccia ActionListener , anche se in tal caso non potrà essere utilizzato per la navigazione ma esclusivamente per eseguire particolari elaborazioni alla pressione di un pulsante oppure di un link. 137 Capitolo IV – I framework a confronto _________________________________________________________________ Evento action Evento action Backing Bean Outcome ActionListener Figura 9 - JSF : Evento "action" In Struts, una situazione di questo tipo viene gestita avviando una action così come in tutti gli altri casi. pressione Action Forward Figura 10 - Struts : Avvio di una action E’ comunque da sottolineare che la presenza dell’evento “action” per la gestione della navigazione non differenzia di molto i due framework, in quanto se da un lato si avvia l’esecuzione di un metodo di un backing bean, dall’altro c’è l’esecuzione di una action. La differenza si evidenzia quando la pressione di un pulsante deve comportare un’elaborazione immediata rimanendo alla medesima pagina (postback). Concettualmente, ciò è possibile con JSF tenendo conto del fatto che sul server è residente lo stato di tutti i componenti come se fosse in esecuzione un’applicazione desktop e quindi la richiesta inviata servirà semplicemente a notificare l’evento da gestire. Con Struts si esegue una normale action, in quanto l’evento è gestito come una tipica richiesta HTTP e si forza il concetto di navigazione sulla stessa pagina. La differenze sostanziali tra una action di Struts ed un ActionListener di JSF possono essere così riassunte : - La action implementa parte della Business-Logic, l’ActionListener rientra nella User Interface Logic; - In una action non si ha l’accesso al componente che intercettato l’evento; viceversa ciò è possibile con un ActionListener; 138 Capitolo IV – I framework a confronto _________________________________________________________________ - La action restituisce sempre il “forward” per la navigazione mentre un ActionListener non partecipa alla fase di navigazione; L’altro tipo di evento previsto in JSF è il “value-change”, che viene scatenato nel momento in cui cambia il contenuto di un componente. Ciò vuol dire che, quando l’utente modifica il valore immesso in un campo ed invia la richiesta, viene scatenato l’evento ed in maniera automatica verrà avviato il listener per la sua gestione. Quest’ultimo può essere realizzato mediante un metodo di un backing bean oppure attraverso una classe che implementa l’interfaccia ValueChangeListener. Evento value-change Backing Bean ValueChangeListener Figura 11 - JSF : Evento "value-change" In Struts, per realizzare un qualcosa di simile, bisogna avviare una solita action che abbia la possibilità di confrontare il valore precedente con il valore attuale del componente ed in caso di differenza gestire l’evento simulato. Tutto ciò avviene in maniera completamente automatica in JSF, considerando che c’è la gestione dello stato dei componenti che viene memorizzato fra una richiesta e l’altra. Ulteriore tipologia di evento prevista in JSF è quella relativa alle fasi che caratterizzano il ciclo di vita di una richiesta. Infatti, l’inizio e la fine di ciascuna fase determinano il verificarsi di un corrispondente evento al quale può essere associato un listener per eseguirne una particolare gestione. In questo modo, si può intervenire in maniera molto specifica durante la manipolazione di una richiesta, in una qualsiasi delle fasi. Ovviamente, questa caratteristica non è prevista in Struts, in cui è possibile intervenire nel ciclo di vita di una richiesta, soltanto prima dell’avvio della sua gestione e non durante. 139 Capitolo IV – I framework a confronto _________________________________________________________________ 6. Mappatura delle richieste sulla Business-Logic Uno degli aspetti che differenzia notevolmente Struts e JSF è soprattutto la modalità con cui tutte le richieste dei client vengono mappate in corrispondenza degli oggetti della business-logic che dovrà gestirle. Il framework Struts prevede un forte utilizzo del file di configurazione XML, in quanto è all’interno di quest’ultimo che viene eseguita l’operazione di “actionmapping”, attraverso la quale si fa corrispondere ad una certa richiesta, pervenuta per un determinato URL, la corrispondente action da eseguire per la sua gestione. Per ogni action, viene inoltre specificato il formbean che conterrà i dati su cui essa dovrà eseguire le elaborazioni. All’arrivo di una richiesta, quindi, grazie al “mapping” caricato in memoria in fase di start-up, viene popolato il formbean con i dati trasmessi ed individuata e successivamente avviata la action per la relativa gestione. L’espandibilità fornita dal framework, prevede che ogni classe che debba gestire una richiesta, estenda la classe Action oppure una delle sue derivate, così come ogni classe che definisce un formbean debba derivare dalla classe ActionForm oppure da una delle classi che la estendono. formbean Action 1 struts-config.xml formbean Action 2 Richiesta Action Mapping formbean Action N Controller Figura 12 - Struts : Mapping delle actions Con JSF, l’approccio è completamente differente, in quanto vengono definiti i backing beans, anche noti come managed beans, all’interno del file di configurazione XML ed ogni richiesta prevede l’esecuzione di uno dei metodi di questi ultimi. Essi sono associati ai componenti dell’interfaccia utente, nel senso che il valore di un 140 Capitolo IV – I framework a confronto _________________________________________________________________ componente può corrispondere alla proprietà di un bean, così come l’evento verificatosi su un componente può comportare l’esecuzione di un metodo del bean stesso. Tutto ciò è garantito mediante il “value-binding” e “method-binding” facendo uso dell’Expression Language. In questo modo, i dati della richiesta vanno a popolare i campi di un backing bean e le elaborazioni da eseguire su di essi sono implementate all’interno di un metodo di quest’ultimo. In pratica, i backing bean sono una combinazione tra i formbean e le action di Struts, non separando operazioni e dati e sono concettualmente simili alle classi di “code-behind” dei WebForms della tecnologia ASP.Net. La loro gestione è completamente automatica e gestita in maniera trasparente dal framework, che si preoccupa di crearne le istanze sulla base dell’ambito di visiblità (scope) che viene specificato nel file di configurazione. faces-config.xml Backing Bean 1 Backing Bean 2 Richiesta CONTROLLER Backing Bean N Figura 13 - JSF : Backing Beans Sulla base di questa differenza tra i due framework, la business-logic può essere collocato nell’applicazione in più modi diversi. Nel caso di Struts, si possono individuare le seguenti possibili soluzioni : 1. si definiscono gli oggetti della business-logic, che utilizzano le classi di accesso alla base di dati, che sono completamente indipendenti dal framework adottato. Le action non fanno altro che acquisire i dati dai 141 Capitolo IV – I framework a confronto _________________________________________________________________ formbean, trasferirli negli oggetti della business-logic ed invocare i metodi su di essi; 2. gli oggetti della business-logic vengono implementati estendendo la classe ActionForm e quindi trattati come formbean. In questo modo, le loro proprietà sono automaticamente popolate dal framework al momento dell’invio della richiesta ed è poi compito delle action invocare su di essi i metodi per le elaborazioni; Action Action formbean Business Object (formbean) Business Object DBAccess Object DBAccess Object Figura 14 – Struts : Locazione della Business-Logic La prima soluzione prevede una marcata indipendenza dagli oggetti della businesslogic rispetto alle classi tipiche del framework, per cui tali oggetti possono essere utilizzati anche per sviluppare l’applicazione con un framework diverso. La seconda soluzione ha il vantaggio di ridurre il numero di classi da adottare, in quanto i formbean diventano gli oggetti della business-logic, ma ha lo svantaggio di rendere non più portabili tali oggetti tra un framework e l’altro. 142 Capitolo IV – I framework a confronto _________________________________________________________________ La tecnologia JSF prevede le seguenti soluzioni : 1. i backing beans coincidono perfettamente con gli oggetti della business-logic. Essi acquisiscono i dati della richiesta nelle loro proprietà ed attraverso i propri metodi eseguono le elaborazioni su di essi; 2. si definiscono gli oggetti della business-logic in maniera indipendente dal framework e poi separatamente si introducono i backing beans, i quali non fanno altro che invocare i metodi dei primi; Business Object (Backing Bean) Backing Bean Business Object DBAccess Object DBAccess Object Figura 15 - JSF : Locazione della Business-Logic La prima soluzione, maggiormente adottata, mette in risalto l’enorme potenza del framework, incapsulando dati ed operazioni , anche se a scapito della riusabilità, in quanto all’interno dei metodi degli oggetti della business-logic si sfruttano le classi tipiche del framework. La seconda soluzione favorisce l’utilizzo delle classi della business-logic con framework diversi, senza necessità di dover riprogettare il software, ma prevede anche una certa duplicazione e ridondanza, in quanto le proprietà dei backing beans saranno presumibilmente uguali alle proprietà degli oggetti della business-logic, così come i loro metodi non faranno nient’altro che invocare i metodi di questi ultimi. 143 Capitolo IV – I framework a confronto _________________________________________________________________ Attraverso l’utilizzo dei managed beans, JSF fornisce il supporto per l’Inversion of Control (IoC), anche noto come Dependency Injection, in quanto le istanze degli oggetti vengono create automaticamente dal framework quando necessario, senza la necessità di intervento da parte dello sviluppatore. In un certo senso, anche Struts fornisce un supporto di questo tipo, istanziando i formbean e le action, ma il concetto di IoC è molto più ampio, così come viene interpretato in JSF. 7. Conversione I dati che l’utente immette nei form, che costituiscono l’interfaccia della Web application, sono generalmente destinati ad essere trasferiti all’interno degli oggetti della business-logic, in maniera diretta o indiretta, per poi essere soggetti alle elaborazioni richieste. Facendo riferimento alla fase di immissione dei dati, all’interno dei campi dell’interfaccia utente, essi sono sempre rappresentati attraverso delle semplici stringhe per poi essere trasferiti al server attraverso la rete. Le proprietà dei bean, a cui i valori sono destinati, possono essere di tipo qualsiasi e non necessariamente di tipo stringa, per cui è richiesta un’operazione di conversione. Per quanto riguarda questo aspetto, il framework JavaServer Faces è ancora una volta superiore rispetto a Struts, mettendo a disposizione dei meccanismi allo stesso tempo potenti e semplicemente espandibili. Infatti, JSF fornisce in primo luogo un insieme di convertitori per i tipi di dato più comuni, ai quali sono associati una serie di tag in modo da poter utilizzare ciascun convertitore all’interno di una pagina JSP, in corrispondenza di un certo componente. Ogni convertitore viene utilizzato sia nella fase di decoding, per trasferire il valore del componente nella corrispondente proprietà del backing bean associato, sia nella fase di encoding per svolgere la funzione inversa. L’operazione di conversione, quindi, prevede di passare da un valore stringa ad un tipo di dato specifico nel primo caso oppure, viceversa, da un certo tipo di dato ad una stringa nel secondo caso. Struts supporta un meccanismo di conversione molto più semplice, implementato attraverso le classi del progetto “common-beanutils” che forniscono supporto per i tipi di dati semplici e non complessi come ad esempio una data, invece supportata da 144 Capitolo IV – I framework a confronto _________________________________________________________________ JSF. Per questo motivo, si preferisce sempre fare in modo che i formbean, che ricevono i dati di input, abbiano tutte le proprietà di tipo stringa in modo che non sia eseguita alcuna conversione a partire dai valori del form. Successivamente, in fase di trasferimento di tali valori verso gli oggetti della business-logic, si eseguono le opportune conversioni. Con JSF, invece, c’è la possibilità di realizzare dei convertitori personalizzati ed i corrispondenti tag, per tipi di dati non previsti dal framework, che magari possono far parte dell’applicazione realizzata. Lo sviluppo di un Custom Converter prevede la realizzazione di una classe che implementa l’interfaccia Converter ed eventualemente, di un Tag Handler per la definizione di un tag con cui associare il convertitore ad un componente in un pagina JSP. Valore Struts Common-beanutils JSF Converter formbean Backing Bean Punto di estensione Figura 16 - JSF e Struts : Conversione 8. Validazione In generale, tutti i sistemi software prevedono un’operazione di validazione, attraverso la quale valutare se i dati immessi dall’utente siano corretti rispetto a ciò che ci si attende. In questo modo, è possibili evitare errori di elaborazione, dovuti a valori non validi, in quanto tale controllo viene eseguito a priori. Per quanto concerne tale aspetto, in riferimento al confronto tra i due framework in questione, si può dire che Struts ha delle potenzialità superiori rispetto a JSF, il quale però fornisce un meccanismo di estensione delle funzionalità di base, molto più semplice. 145 Capitolo IV – I framework a confronto _________________________________________________________________ In Struts, le funzionalità di base per la validazione sono integrate all’interno dei formbean, in quanto la classe ActionForm prevede un metodo validate(), del quale eseguire l’overriding per potervi immettere il codice che effettui la validazione dei dati del form stesso. Action formbean validate() Figura 17 - Struts : Validazione formbean con il metodo validate() Ovviamente, questo tipo di approccio non può essere adottato per i formbean dinamici, che non prevedono l’implementazione di una classe. Per questo motivo, nella maggior parte dei casi, la validazione dei dati è realizzata sfruttando un plug-in esterno, noto come Validator, che è stato integrato nel framework a partire dalla versione 1.1. Tale plug-in si basa sull’utilizzo di due file di configurazione XML, i quali permettono rispettivamente di : - dichiarare quali sono i form ed i corrispondenti campi che dovranno essere soggetti alla validazione e quali saranno le funzioni di validazione da adottare per ciascuno di essi; - definire tali funzioni, dette anche regole di validazione; Il plug-in Validator mette a disposizione una serie di regole di validazione predefinite più comuni : dal controllo che il valore di un campo non sia vuoto fino al controllo che un valore, che dovrebbe rappresentare un indirizzo email oppure il codice di carta di credito, abbia un formato corretto passando per i semplici controlli per cui un valore immesso sia di un certo tipo oppure soddisfi un certo pattern. Ovviamente, 146 Capitolo IV – I framework a confronto _________________________________________________________________ è possibile integrare queste regole, realizzando delle funzioni personalizzate che permettano di eseguire delle operazioni di validazione non previste dal plug-in. Action formbean Validator PlugIn validation-rules.xml validation.xml Figura 18 - Struts : Validazione formbean con il plug-in Validator Il framework JSF, invece, fornisce soltanto tre validatori standard relativi al controllo che un certo valore rientri in un range oppure che abbia una lunghezza compresa tra un minimo ed un massimo prefissati. Per poter eseguire delle operazioni di validazione personalizzate, è possibile utilizzare i metodi dei backing beans oppure realizzare delle classi che implementano l’interfaccia di base Validator. Valore Validator Backing Bean Punto di estensione Figura 19 - JSF : Validazione Per quanto concerne questo aspetto del confronto, Struts fornisce numerosi validatori standard ed inoltre, oltre alla validazione lato server, supporta la validazione lato client, non prevista in JSF. Infatti, nella libreria HTML è fornito un 147 Capitolo IV – I framework a confronto _________________________________________________________________ tag che ha la capacità di generare in maniera automatica il codice Javascript di una qualsiasi regola di validazione. 9. Navigazione Le applicazioni Web, sviluppate prima dell’introduzione di framework specifici, hanno sempre previsto la cosiddetta navigazione statica nell’ambito della quale, per poter passare da una pagina all’altra, è necessario utilizzare i collegamenti ipertestuali, specificando per ciascuno di essi la pagina di destinazione. Struts e JavaServer Faces introducono il concetto di navigazione dinamica, in cui la pagina di destinazione è determinata in seguito ad una elaborazione eseguita dalla business-logic. Inoltre, in entrambi i framework, la navigazione attraverso le pagine è specificata all’interno del file di configurazione, per cui si parla di “navigazione dichiarativa”, anche se utilizzando due modalità differenti. Il framework JSF prevede tre elementi fondamentali : - pagina di origine; - outcome; - pagina di destinazione; che vengono definiti attraverso le regole di navigazione (navigation-rules) ed i “casi” di navigazione (navigation-case). Più precisamente, ciascuna regola di navigazione è caratterizzata dalla pagina di origine e da una o più pagine di destinazione, verso le quali proseguire la navigazione sulla base dell’outcome che viene manipolato. Quest’ultimo può essere specificato direttamente all’interno di un tag di una pagina JSP, relativamente ad un link oppure ad un bottone, oppure può essere determinato in seguito all’esecuzione di un metodo di un backing bean. Nota la pagina di origine, la classe NavigationHandler si fa carico di acquisire l’outcome prodotto e cercarlo tra i casi di navigazione specificati nella copia in memoria del file di configurazione. Una volta trovato l’outcome, ad esso corrisponderà la pagina di destinazione verso la quale redirezionare il flusso dell’applicazione. Tale classe è assolutamente espandibile, permettendo allo sviluppatore di eseguire delle operazioni preliminari al proseguimento della navigazione. 148 Capitolo IV – I framework a confronto _________________________________________________________________ Outcome Start Page faces-config.xml NavigationRule 1 Start Page NavigationHandler NavigationCase 1 Outcome 1 Dest Page 1 NavigationCase N Outcome N Destination Page Dest Page N Figura 20 - JSF : Navigazione In Struts, la navigazione si basa sul concetto di “forward” che può essere locale oppure eventualmente globale. Infatti, una volta avviata l’esecuzione del metodo execute() di una action, quest’ultimo produce in uscita un oggetto ActionForward che individua appunto un elemento XML di tipo “forward” definito nel file di configurazione. Tale elemento avrà un nome e la pagina di destinazione verso la quale proseguire la navigazione. Esso può essere locale, nel senso che può essere riferito solo ed esclusivamente da una certa action, oppure globale in quanto può essere condiviso tra più action differenti. 149 Capitolo IV – I framework a confronto _________________________________________________________________ struts-config.xml ActionForward Action 1 CONTROLLER Forward 1 Dest Page 1 Forward N Dest Page N Destination Page Global Forwards Forward 1 Dest Page 1 Figura 21 - Struts : Navigazione 10. Expression Language L’Expression Language è un linguaggio attraverso il quale è possibile fare riferimento ai JavaBeans previsti dall’applicazione, all’interno dei tag di una pagina JSP. L’utilizzo di questo linguaggio è previsto in forma nativa nel framework JavaServer Faces ma non in Struts. La maggior parte dei tag che caratterizzano le librerie di JSF, in corrispondenza di specifici attributi, supportano il “value-binding” ed il “method-binding”, attraverso i quali si può fare riferimento alle proprietà oppure ai metodi dei backing bean. Tale riferimento viene esplicitato attraverso l’utilizzo dell’Expression Language, facendo 150 Capitolo IV – I framework a confronto _________________________________________________________________ uso della tipica “dot notation” che, attraverso un punto, separa il nome del bean dalla proprietà o dal metodo a cui si è interessati. TAG JSF Attributo #{backingBean.proprieta} Value-binding BackingBean proprieta TAG JSF metodo Attributo #{backingBean.metodo} Method-binding Figura 22 - JSF : Value/Method binding con l' EL Questa potenzialità non è prevista in Struts, se non attraverso l’utilizzo delle librerie JSTL (JavaServer Pages Standard Tag Library) oppure attraverso un’ estensione particolare delle librerie proprie del framework. La prima soluzione prevede di utilizzare i tag appartenenti alle librerie JSTL che sono completamente indipendenti dal framework. La modalità di utilizzo dell’EL all’interno di questi tag è la stessa di JSF. La seconda soluzione prevede di utilizzare le stesse librerie di Struts ma con l’estensione all’Expression Language. Infatti, le tre librerie di base previste non supportano tale linguaggio, ma ne sono state create delle versioni “evolute”, con suffisso “el”, che forniscono le medesime funzionalità delle prime, ma con in più la possibilità di utilizzare l’Expression Language in corrispondenza dei propri attributi. E’ comunque da sottolineare che, seppure utilizzando l’EL, con Struts non ci si può riferire ad un metodo di un bean, in quanto è sempre prevista l’esecuzione di una action. 151 Capitolo IV – I framework a confronto _________________________________________________________________ TAG Struts Attributo Attributo proprieta bean Struts Bean proprieta TAG JSTL / Struts-EL Attributo #{bean.proprieta} Struts-EL/JSTL Figura 23 - Struts : Riferimenti ai beans con e senza EL In relazione a tale aspetto, si può dire che JSF è avvantaggiato rispetto a Struts perché, mentre il primo fornisce il supporto dell’EL in forma nativa, con il secondo è necessario l’utilizzo di librerie esterne o estensioni di quelle interne. 11. Eccezioni Uno degli aspetti che rappresenta un netto vantaggio di Struts rispetto a JSF, è la gestione delle eccezioni. Infatti, il framework Struts permette di dichiarare, all’interno del file di configurazione, le eccezioni che potranno essere sollevate dall’applicazione ed i corrispondenti gestori. 152 Capitolo IV – I framework a confronto _________________________________________________________________ In particolare, sono previste le due seguenti classi : ModuleException : permette di definire una eccezione alla quale associare un - messaggio di errore prelevato dal Resource Bundle; ExceptionHandler : permette di gestire una qualsiasi eccezione dichiarata nel - file di configurazione; In pratica, all’interno del file struts-config.xml può essere dichiarata un’eccezione che tipicamente è del tipo ModuleException oppure una classe che la estende. Inoltre, ad essa viene associato un gestore che può essere quello di default oppure una classe che estenda ExceptionHandler. Nel momento in cui, durante l’esecuzione dell’applicazione, viene sollevata un’eccezione di questo tipo, essa viene intercettata e gestita in maniera completamente automatica, senza la necessità di blocchi try-catch-finally all’interno del codice. Exception ExceptionHandler struts-config.xml Global Exceptions Exception 1 Error Page 1 Exception N Error Page N Error Page Figura 24 - Struts : Eccezioni dichiarative Questa potenzialità non è assolutamente prevista in JSF, nell’ambito del quale le eccezioni devono essere completamente gestite dallo sviluppatore, secondo le funzionalità offerte dal linguaggio Java. 153 Capitolo IV – I framework a confronto _________________________________________________________________ BackingBean metodo catch (Exception) Outcome NavigationHandler Error Page Figura 25 - JSF : Eccezioni programmatiche 12. Internazionalizzazione (I18N) L’internazionalizzazione è una caratteristica comune di JSF e Struts che viene praticamente gestita allo stesso modo. Infatti, entrambi i framework offrono la possibilità di definire uno o più Resource Bundle (Message Resource), all’interno dei quali ci sono i messaggi di testo che devono essere visualizzati nelle pagine dell’applicazione, in lingue diverse. In questo modo, sulla base della locazione geografica dell’utente, il sistema riesce ad utilizzare il Resource Bundle corretto, contenente i messaggi nella lingua adatta. L’accesso al file .properties, contenete i messaggi, è effettuato allo stesso modo ossia specificando la chiave (key) associata al messaggio da visualizzare. Il meccanismo può essere schematizzato in questo modo : - il Locale (localizzazione geografica dell’utente) permette di individuare il Resource Bundle da utilizzare; - la chiave (key) permette di individuare il messaggio da visualizzare, nell’ambito del Resource Bundle precedentemente selezionato; 154 Capitolo IV – I framework a confronto _________________________________________________________________ Key = msg.benvenuto Message Resources Resource Bundle IT Benvenuto ! Resource Bundle EN Welcome ! Resource Bundle FR Bienvenu ! Resource Bundle DE Willkommen ! Locale Figura 26 - JSF e Struts : Internazionalizzazione 13. Sicurezza Una delle più importanti garanzie che deve fornire una Web application ai propri visitatori è la sicurezza nella comunicazione e nello scambio dei dati. In moltissimi casi, l’applicazione prevede il trasferimento di informazioni sensibili, come ad esempio il numero di una carta di credito, per le quali si rende necessaria un’operazione di crittografia. Mediante quest’ultima, è possibile rendere i dati illeggibili a chiunque li intercetti durante il trasferimento, a meno del destinatario che ha a propria disposizione la chiave per decrittografarne il contenuto. Il meccanismo che permette di garantire la sicurezza nelle comunicazioni in rete prevede l’utilizzo del protocollo SSL (Secure Sochet Layer) ed in particolare del protocollo HTTPS (HTTP basato su SSL). Per quanto riguarda questo aspetto, i due framework a confronto sono perfettamente uguali, in quanto non prevedono approcci differenti per garantire la sicurezza delle pagine delle Web application realizzate. La tecnica adottata è quella comune a tutte le applicazioni Web implementate in Java e fa uso delle funzionalità messe a disposizione dal Web Container. La sicurezza, quindi, è 155 Capitolo IV – I framework a confronto _________________________________________________________________ gestita allo stesso modo, in quanto tale compito è completamente demandato all’ambiente di esecuzione delle applicazioni, all’interno del quale viene abilitato il supporto al protocollo SSL. Le Web application prevedono alcune semplici modifiche al Deployment Descritpor web.xml, all’interno del quale va specificato il livello di protezione e quali sono le pagine che ne devono essere soggette. In questo modo, ogni qual volta arriva una richiesta ad una pagina tra quelle suddette, il Web Container esegue la redirezione sulla porta impostata per il protocollo SSL e la trasmissione avverrà attraverso l’HTTPS. Richiesta pagina SSL Web Application Web Container Deployment Descriptor Connector HTTPS Pagine SSL https://WebApplication/... Figura 27 - JSF e Struts : Sicurezza 14. Configurazione La configurazione di una Web application, realizzata con Struts oppure JSF, è uno degli aspetti in cui i due framework sono piuttosto simili. Infatti, entrambi prevedono la configurazione attraverso un file XML, per quanto concerne tutte le caratteristiche principali dell’applicazione. Nella fase di start-up, per ciascuno di essi è prevista un’operazione di lettura e parsing del file, per poter generare una copia in 156 Capitolo IV – I framework a confronto _________________________________________________________________ memoria delle informazioni di configurazione all’interno di specifiche classi. Ciò che cambia tra i due framework è soltanto la sintassi XML ed ovviamente le caratteristiche che possono essere configurate, che dipendono dalle potenzialità che ha ciascuno dei due. Classi in memoria struts-config.xml ApplicationConfig Configurazione ActionConfig … … … … parsing ControllerConfig faces-config.xml Application Configurazione Lifecycle … … … … parsing NavigationHandler Figura 28 - JSF e Struts : Configurazione 15. Web Application Layout Per quanto riguarda la definizione del layout delle pagine che compongono l’applicazione, la distribuzione di Struts ingloba il framework Tiles. Quest’ultimo permette di definire la struttura di una pagina JSP, mediante una tecnica di composizione di più parti separate, i “tiles” appunto, che sono praticamente ulteriori pagine JSP. In questo modo, è possibile evitare la replicazione dei contenuti e 157 Capitolo IV – I framework a confronto _________________________________________________________________ facilitare la manutenzione, in quanto la modifica di un gruppo di informazioni va eseguita una ed una sola volta. JavaServer Faces non dispone di questo supporto, però è comunque possibile importare le librerie di Tiles all’interno di un progetto JSF, per poterne sfruttare le potenzialità. Potendo utilizzare Tiles con entrambi i framework, sembrerebbe non esserci alcuna differenza tra essi sotto questo punto di vista. In realtà, essendo Tiles inglobato in Struts, c’è una perfetta integrazione con la modalità di navigazione prevista da quest’ultimo. Infatti, se un’applicazione realizzata in Struts prevede l’utilizzo di Tiles, la classe che si occupa delle gestione delle richieste, non è esattamente la RequestProcessor ma la più specifica TilesRequestProcessor, contenuta nelle librerie di Tiles. Questa classe gestisce le richiesta allo stesso modo della precedente e garantisce una perfetta sinergia con le operazioni di forwarding tra le pagine, così come previsto in Struts. Inoltre, la sua dichiarazione viene effettuata all’interno del file di configurazione di Struts come se fosse semplicemente un plug-in. CONTROLLER Richiesta TilesRequestProcessor Figura 29 - Struts : Integrazione con Tiles Invece, per poter utilizzare Tiles all’interno di JSF, è necessario dichiararlo nel Deployment Descriptor attraverso una Servlet, la nota TilesServlet, che viene avviata immediatamente dopo la FacesServlet. Ciò mette in evidenza che, mentre in Struts le classi di quest’ultimo e Tiles comunicano internamente al framework, in JSF invece si ha a che fare con due Servlet indipendenti che scambiano informazioni. Richiesta CONTROLLER TilesServlet FacesServlet Figura 30 - JSF : Interazione con la TilesServlet 158 Capitolo IV – I framework a confronto _________________________________________________________________ 16. Migrazione da Struts a JavaServer Faces In passato, molti sviluppatori hanno scelto Struts per la realizzazione di Web application, sulla base delle notevoli funzionalità offerte da quest’ultimo e dai tools di sviluppo forniti dalle case produttrici. Ad oggi, in virtù dell’introduzione del framework JavaServer Faces, ogni team di sviluppo deve prendere in considerazione la possibilità di migrare verso questa nuova tecnologia, in riferimento ad un’applicazione realizzata con Struts. La migrazione è consigliata soprattutto nel caso in cui, la Web application debba essere ulteriormente potenziata, nel qual caso si può fare uso degli strumenti innovativi di JSF. Nel caso in cui, debba essere previsto esclusivamente un processo di manutenzione, si può pensare di mantenere l’architettura attuale realizzata in Struts. 16.1 Strategie di migrazione L’architettura fortemente espandibile di JSF mette a disposizione diversi approcci per poter essere adottata in applicazioni preesistenti. L’approccio maggiormente conservativo prevede semplicemente di utilizzare i componenti di JSF, senza alcun altra caratteristica del framework. L’altra possibilità è quella di eseguire una migrazione incrementale oppure completa. 16.1.1 Components Only Utilizzare i componenti JSF in un’applicazione realizzata con un altro framework, fornisce il vantaggio di poter sfruttare componenti eventualmente già presenti sul mercato, senza la necessità di riscriverli per la propria applicazione. Tutto ciò comporta un enorme beneficio, soprattutto nel caso in cui si ha a che fare con un’applicazione di grandi dimensioni e tempi di sviluppo piuttosto stringenti. Struts mette a disposizione una libreria chiamata Struts-Faces, mediante la quale è possibile costruire componenti della UI come in JSF, ma continuando ad utilizzare contemporaneamente le action. Ciò è possibile grazie a particolari listeners JSF, che 159 Capitolo IV – I framework a confronto _________________________________________________________________ entrano in gioco garantendo l’esecuzione del tipico ciclo di vita di una richiesta previsto in Struts, in maniera del tutto trasparente rispetto all’esecuzione delle action. Questo tipo di evoluzione, è possibile in virtù del fatto che sia Struts che JSF sono implementati mediante l’utilizzo delle proprie Servlets che sono in esecuzione all’interno della stessa applicazione, in modo da poter condividere le variabili e la logica applicativa. Client Faces request Faces response FacesServlet Event Listeners Struts response Struts request ActionServlet Specialized RequestProcessor Figura 31 - Migrazione "Components Only" Ogni Struts request viene inoltrata verso la ActionServlet, la quale non utilizza il RequestProcessor di default, ma bensì una versione specifica, per poter intercettare eventuali eventi sui componenti JSF. Se la richiesta non prevede l’intervento di questi ultimi, viene prodotta una Struts response. Viceversa, se viene scatenato quale evento su un componente della UI, realizzato con JSF, il controllo passa alla FacesServlet la quale produrrà una Faces response. E’ ovvio che se a monte, arriva una Faces request, essa viene gestita direttamente dalla FacesServlet, la quale attraverso i listeners, passa il controllo al RequestProcessor che avvierà le action Struts per la gestione della richiesta. Al termine dell’esecuzione, si potrebbe generare una Struts response oppure una Faces response. 160 Capitolo IV – I framework a confronto _________________________________________________________________ E’ da mettere in evidenza che, grazie all’utilizzo di componenti JSF, si elimina il limite di Struts che riguarda la non indipendenza dal dispositivo client. Infatti , è possibile accedere alla Web application, non necessariamente attraverso un browser Web ma eventualmente mediante cellulari, palmari o dispositivi di questo genere. 16.1.2 Incremental migration Quando si ha come obiettivo una migrazione verso l’architettura JSF, il primo approccio può essere quello incrementale, mediante il quale si possono trarre i principali vantaggi di JSF ma con un impatto minimo sull’infrastruttura preesistente. Tale migrazione è utile quando c’è la necessità di introdurre nuove funzionalità nell’applicazione e contemporaneamente eseguire l’upgrade verso JSF. La migrazione incrementale prevede come primo passo l’utilizzo di componenti JSF, così come la component-only, e poi successivamente lo spostamento della businesslogic all’interno dei backing bean. Inoltre, essa può essere eseguita convertendo parte dell’application logic dopo aver realizzato le pagine basate sui componenti JSF, oppure eseguire queste operazioni contemporaneamente. Ciò è possibile perché tutto il codice necessario è all’interno delle stessa applicazione, per cui le parti che utilizzano JSF e le parti che utilizzano un altro framework, come Struts, condividono i medesimi oggetti. Dalla figura seguente, si osserva che gli event-listeners di JSF e le classi della logica applicativa di un altro framework, in questo caso le action di Struts, condividono le medesime classi della business-logic e le classi per l’accesso alla base di dati. Tutto ciò garantisce la convivenza di due framework nell’ambito della medesima applicazione. 161 Capitolo IV – I framework a confronto _________________________________________________________________ Client Faces request Faces response Non-Faces request Non-Faces response FacesServlet Non-FacesServlet Business-Logic & Data Access Event Listeners Application Logic State & Application Beans Web Application Figura 32 - Migrazione : Incremental migration Nel caso di una Faces request, la FacesServlet utilizza i listener degli eventi che sfruttano i beans e gli oggetti della business-logic, oltre che dell’accesso alla base di dati. Nel caso di un Non-Faces request, per esempio orientata a Struts, ci sarà una certa logica applicativa, le action nel caso di Struts, che accederà ai medesimi oggetti suddetti. Si riesce a far convivere porzioni di codice di JSF e di Struts, facendo riferimento agli stessi oggetti della business-logic. Di seguito, si evidenziano le corrispondenze che ci sono tra Struts e JSF e quindi quali sono i compiti da svolgere, nel caso di una migrazione incrementale dall’uno all’altro. 162 Capitolo IV – I framework a confronto _________________________________________________________________ Struts Configuration Migration Layers (forwards, actions, etc.) JavaServer Faces Configuration (navigation rules, backing beans, etc.) JSP pages JSP pages (with Struts tags) (with JSF component tags) Application Layer Application Layer (actions, action forms,etc.) Business Layer (JBs, EJBs, Web services) Integration Layer (Data Access Objects) Data Service (DataBase, JBs, EJBs, Web services) (backing beans, event listeners, etc.) Business Layer (JBs, EJBs, Web services) Integration Layer (Data Access Objects) Data Service (DataBase, JBs, EJBs, Web services) Figura 33 - Migrazione : Migration Layers Come si può osservare, i livelli contenenti la business-logic e le classi per l’accesso alle basi di dati, sono perfettamente coincidenti e quindi non c’è bisogno di alcun accorgimento nel passaggio da un framework all’altro. 163 Capitolo IV – I framework a confronto _________________________________________________________________ Ciò che ovviamente va rivisto sono : - il livello applicativo : in Struts sono previste le action ed i formbean, mentre in JSF i backing beans ed i listeners; - le pagine JSP : Struts fornisce i tag delle proprie librerie, neutre JSF mette a disposizione i componenti evoluti della UI; - configurazione : i file di configurazione XML utilizzano degli elementi completamente differenti; 16.1.3 Full migration La migrazione può essere eseguita in maniera completa in una sola volta, al posto di una migrazione incrementale in più passi successivi. Questo tipo di soluzione può essere adottata quando i tempi di sviluppo a disposizione sono piuttosto ampi, in quanto deve essere rivista la completa architettura dell’applicazione. Dal punto di vista concettuale, essa è simile alla migrazione incrementale, considerando i livelli da modificare o meno, passando da Struts a JSF. La differenza sostanziale sta nel fatto che non è necessario utilizzare una libreria di integrazione, in quando il vecchio framework viene completamente sostituito. 164 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Capitolo V – Case Study : Analisi e Progettazione 1. Introduzione La presentazione dei due principali framework, quali Struts e JavaServer Faces, utilizzati per la realizzazione di Web application ha come obiettivo principale la possibilità di poterne effettuare un confronto. Una volta evidenziate le potenzialità dell’uno e dell’altro, si rende necessario lo sviluppo di un’applicazione con ciascuno di essi, in modo da individuare pregi e difetti di entrambi e vantaggi o svantaggi dell’uno rispetto all’altro. Tale confronto è relativo a tutti gli aspetti che riguardano la realizzazione di una Web application, con strumenti che si basano sul pattern MVC, quali appunto i framework presi in considerazione. In questa sede, è stata realizzata un’applicazione Web che permetta agli utilizzatori di usufruire di contenuti audio, quali brani musicali in formato MP3 (MPEG Layer 3), mettendo a disposizione degli utenti e dell’amministratore del sistema una serie di funzionalità definite dalle specifiche assegnate. La Web application è stata realizzata con l’utilizzo di entrambi i framework, usufruendo , lì dove possibile, di tutte le potenzialità di ciascuno di essi. Le fasi di sviluppo hanno seguito il tipico ciclo di vita del software secondo il modello “a cascata”, Waterfall Model, e possono essere riassunte di seguito : - studio di fattibilità; - analisi e specifica dei requisiti; - progettazione; - codifica ed implementazione; - testing; - messa in esercizio; - manutenzione; Lo studio di fattibilità, in questo caso, è stato immediatamente superato, in quanto non c’erano da fare considerazioni riguardo le possibili soluzioni da adottare, le 165 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ risorse finanziarie ed umane a disposizione, tenendo conto che sono stati fissati a priori gli strumenti da utilizzare. L’Analisi e la specifica dei requisiti ha previsto la realizzazione del Documento di Specifica dei Requisiti Software (SRS – Software Requirements Specification), elencando tutte le funzionalità fornite dal sistema software sia all’utente che all’amministratore e considerando per ciascuna di esse, una breve descrizione, gli input previsti, le elaborazioni eseguite ed i possibili output prodotti. Nella fase di progettazione, sono stati realizzati i principali diagrammi UML (Unified Modelling Language) previsti per lo sviluppo, quali : - Class Diagram; - Use Case Diagram; - Sequence Diagram; - Activity Diagram; - Statechart Diagram; Ad essi, vanno poi aggiunti il Conceptual Data Model ed il Physical Data Model, per la descrizione concettuale e fisica della base di dati utilizzata dalla Web application. Lo sviluppo ha poi previsto due distinte fasi di codifica, realizzando l’applicazione con entrambi i framework. Facendo riferimento alla struttura del Model prevista dal pattern MVC, il sottolivello di Data Access è stato realizzato separatamente dagli altri e le classi che ne sono scaturite, sono state utilizzate per entrambe le implementazioni. Per quanto concerne i sottolivelli superiori, Business-Logic e External Interface, si è preferita una modalità di “fusione”, più legata a JavaServer Faces che non a Struts. La fase di testing ha permesso la correzione di errori e problemi rilevati durante il funzionamento del software. Infine, non sono state previste delle vere e proprie fasi di messa in esercizio e manutenzione. Lo strumento software di tipo CASE, che è stato utilizzato per la realizzazione di tutti i diagrammi della fase di progettazione, è il noto Sybase Power Designer. 166 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 2. Requisiti Il case study (caso di studio) preso in esame, prevede lo sviluppo di una Web application che permette all’utenza in generale, di usufruire di contenuti audio, quali brani musicali in formato MP3 (MPEG Layer 3). Gli utilizzatori possono avere permessi di accesso diversi e quindi funzionalità differenziate, sulla base della distinzione dei seguenti ruoli : - utente; - amministratore; Per ciascuno di essi, le funzionalità accessibili nell’applicazione sono descritte di seguito. Utente : - registrazione “base” per l’ascolto in streaming dei brani musicali e la gestione di playlist personalizzate; - registrazione “estesa” che permetta l’acquisto dei brani musicali e la possibilità di eseguirne il download, oltre alle funzionalità di base; - acquisto e successive ricariche di una scheda prepagata “virtuale”, mediante pagamento con carta di credito, che permetta l’acquisto dei brani musicali; - accesso all’archivio musicale, con possibilità di fissare diversi criteri di ricerca dei brani desiderati e di visionarne le informazioni, per poterne eseguire l’ascolto, l’acquisto ed il download; - gestione di una o più playlist, con la possibilità di ascolto, nonchè di aggiungere ed eliminare da ciascuna di esse dei brani musicali; - gestione dei propri dati personali per un eventuale aggiornamento; - richiesta di una mail, contenente username e password di accesso, in caso di dimenticanze; - operazioni di login e logout; 167 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore : - gestione completa dell’archivio musicale, con la possibilità di inserire, modificare e cancellare i brani, usufruendo della funzionalità automatica del sistema di garantire coerenza tra i tag ID3v1 degli stessi e le informazioni immesse nella base di dati; - accesso all’archivio musicale, con possibilità di fissare diversi criteri di ricerca dei brani desiderati e di visionarne le informazioni, per poterne eseguire l’ascolto; - gestione degli utenti registrati, con la possibilità di visionarne i dati ed eseguire le operazioni di cancellazione e sblocco, qualora un utente abbia commesso più di due tentativi errati di accesso al sistema; - gestione dei propri dati di accesso per un eventuale aggiornamento; - operazioni di login e logout; Sulla base di tali requisiti è stato redatto il Documento di Specifica dei Requisiti Software (SRS), di fondamentale importanza per le fasi successive di sviluppo. 3. Progettazione La fase di progettazione ha previsto la realizzazione dei principali diagrammi UML, in relazione soprattutto all’analisi del software e non tanto alla sua corrispondente implementazione. Essi costituiscono un’astrazione di alto livello del sistema e sono completamente indipendenti da quelli che possono essere gli strumenti utilizzati nella codifica. 3.1 Use Case Diagrams I diagrammi dei casi d’uso (Use Case Diagrams) costituiscono uno strumento utile per catturare il comportamento esterno del sistema da sviluppare, senza dover specificare come tale comportamento debba essere realizzato; il sistema è visto come una scatola nera (black-box). Essi forniscono una descrizione dei 168 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ “modi” in cui il sistema potrà essere utilizzato, ossia ciò che l’utente può fare su di esso e come quest’ultimo risponde alle sollecitazioni. La realizzazione dei diagrammi ha previsto un approccio top-down, partendo dallo scenario di carattere generale, in cui le modalità d’uso logicamente correlate sono accorpate, fino ad una decomposizione in cui sono descritti i casi di utilizzo non ulteriormente scomponibili. 3.1.1 Generale Facendo riferimento ai requisiti assegnati, è stato definito un Use Case Diagram Generale, che si pone al livello di astrazione più elevato del sistema, mettendo in evidenza tutte quelle che sono le modalità di utilizzo dello stesso, da parte dell’utente e dell’amministratore. Questi ultimi rappresentano gli attori che possono accedere alle funzionalità offerte dal sistema, di cui alcune sono condivise ossia accessibili da entrambi, mentre altre sono prerogativa esclusiva di uno dei due ruoli. Verifica Copertura Carta Credito <<include>> Registrazione Gestione Scheda Prepagata Gestione Dati Personali Amministratore Login Sistema Utente Logout Sistema Azioni su brani MP3 Gestione Utenti Gestione Archivio brani MP3 Figura 1 - Use Case Diagram Generale 169 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ A partire da questo diagramma, è possibile effettuare una decomposizione scendendo ad un livello di astrazione minore, in cui si prendono in considerazione nel dettaglio le modalità di utilizzo del sistema. In particolare, le modalità accessibili esclusivamente dall’utente sono le seguenti : - Gestione Scheda Prepagata; - Registrazione; - Azioni su brani MP3; 3.1.2 Gestione Scheda Prepagata La modalità di “Gestione Scheda Prepagata” è relativa a tutte le azioni che l’utente può eseguire in relazione alla scheda prepagata, ossia l’acquisto, la ricarica e la visualizzazione delle informazioni. Acquisto Scheda Prepagata <<include>> Verifica Copertura Carta Credito <<extend>> Ricarica Scheda Prepagata Utente Visualizzazione Informazioni Scheda Figura 2 - Use Case Diagram Gestione Scheda Prepagata 170 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.1.3 Registrazione La modalità di “Registrazione” può essere considerata atomica e non ulteriormente decomponibile, facendo riferimento alla procedura eseguita dall’utente per registrarsi ed usufruire dei servizi della Web application. 3.1.4 Azioni su Brani Infine, la modalità delle “Azioni su brani MP3” è da considerarsi la più complessa, in quanto al suo interno prevede tutte le possibili azioni che l’utente può eseguire all’interno dell’applicazione sui contenuti audio offerti. In particolare, sono da evidenziare le opportunità di visualizzare le informazioni di un brano, l’ascolto in streaming di quest’ultimo, il relativo acquisto e download, nonché la visualizzazione dell’elenco dei brani disponibili per genere oppure in base a dei criteri di ricerca prefissati. E’ prevista inoltre la modalità “Gestione Playlist”, che non può essere considerata atomica ma che è possibile ulteriormente decomporre scendendo ad un livello di astrazione molto più basso. Acquisto/Download brano MP3 <<include>> Ascolto brano MP3 <<include>> Visualizzazione Informazioni brano MP3 Utente Ricerca brani MP3 <<include>> Gestione Playlist <<extend>> Visualizzazione brani MP3 Figura 3 - Use Case Diagram Azioni su Brani MP3 171 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.1.5 Gestione Playlist Per quanto riguarda la modalità “Gestione Playlist”, essa comprende tutte le azioni che l’utente può eseguire sulle playlist, in particolare : - creazione di una nuova playlist specificandone il nome; - visualizzazione dell’elenco delle playlist create in precedenza dall’utente; - visualizzazione dei brani MP3 contenuti in una specifica playlist; - azioni di visualizzazione e ricerca dei brani all’interno dell’archivio, con la possibilità di aggiungere uno o più di essi all’interno di una playlist specificata; - possibilità di rimuovere uno o più brani da una playlist; - ascolto della playlist; - cancellazione di uno o più playlist; Sulla base dei suddetti modi d’uso, lo Use Case Diagram assume una forma abbastanza complessa ed ampia. Osservandone la struttura, si evidenzia la presenza di due scenari distinti associati alle modalità d’uso di cancellazione di una playlist e di inserimento di un brano MP3. Nel primo caso, è possibile cancellare una o più playlist selezionandole dall’elenco, oppure visualizzare l’elenco dei brani di una playlist e richiedere la cancellazione di quest’ultima. Nel secondo caso, è possibile inserire uno o più brani MP3 selezionandoli da un elenco, visualizzato al termine di una ricerca, oppure inserire un singolo brano dalla schermata di visualizzazione delle informazioni corrispondenti. Secondo la sintassi UML, entrambi i casi d’uso “includono” le modalità di utilizzo che danno luogo ai due scenari suddetti. 172 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Ha due scenari alternativi, in base alla visualizzazione di più playlist o della singola playlist Visualizzazione Elenco Playlist Creazione Playlist <<include>> Cancellazione Playlist Ricerca brani MP3 <<extend>> Ascolto Playlist Visualizzazione brani MP3 <<include>> <<include>> <<include>> Utente Inserimento brano MP3 Playlist Visualizzazione brani MP3 Playlist <<include>> <<include>> <<include>> Cancellazione brano MP3 Playlist Ha due scenari alternativi, in base alla visualizzazione di più brani o del singolo brano <<include>> Visualizzazione Informazioni brano MP3 Figura 4 - Use Case Diagram Gestione Playlist 3.1.6 Gestione Utenti Considerando nuovamente il diagramma dei casi d’uso Generale, si evince che ci sono le modalità “Gestione Utenti” e “Gestione Archivio brano MP3” che sono accessibili solo ed esclusivamente dall’amministratore. 173 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ In particolare, “Gestione Utenti” comprende alcune azioni basilari che possono essere eseguite sugli utenti registrati, in particolare la visualizzazione di un elenco di questi ultimi, la cancellazione, la visualizzazione dei dati anagrafici di un singolo utente e l’operazione di sblocco, qualora l’utente abbia commesso due tentativi errati consecutivi di accesso al sistema e sia stato bloccato. Visualizzazione Dati Anagrafici Utente <<include>> Sblocco Utente <<include>> Cancellazione Utente Ha due scenari alternativi, in base alla visualizzazione di più utenti o del singolo utente <<include>> Amministratore <<include>> Visualizzazione Utenti Figura 5 - Use Case Diagram Gestione Utenti 3.1.7 Gestione Archivio Brani La modalità d’uso più complessa è certamente la “Gestione Archivio Brani MP3”, nell’ambito della quale, l’amministratore esegue tutte le operazioni di gestione dell’archivio del sistema contenente i brani in formato MP3. In particolare, le operazioni possibili sono le seguenti : - visualizzazione dei brani in base ad un genere oppure sulla base di criteri di ricerca opportunamente fissati; - visualizzazione e modifica delle informazioni di un brano; 174 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ - inserimento di un nuovo brano MP3 in archivio con l’upload del file MP3 associato; - cancellazione di uno o più brani dall’archivio; - ascolto di un singolo brano MP3; Visualizzazione Informazioni brano MP3 <<include>> <<include>> Ascolto brano MP3 Ha due scenari alternativi, in base alla visualizzazione di più brani o del singolo brano <<include>> Cancellazione brano MP3 <<include>> Amministratore Visualizzazione brani MP3 <<include>> Inserimento brano MP3 <<extend>> <<include>> Ricerca brani MP3 Modifica Informazioni brano MP3 Figura 6 - Use Case Diagram Gestione Archivio Brani MP3 175 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.1.8 Casi d’uso comuni : Login, Logout, Gestione Dati Personali Infine, le modalità di utilizzo del sistema che sono comuni all’utente ed all’amministratore, riguardano il “Login Sistema” ed il “Logout Sistema” che possono essere considerate atomiche e la “Gestione Dati Personali” che comprende le operazioni di gestione dei propri dati di registrazione e di accesso per entrambi i ruoli. In particolare, l’amministratore ha la possibilità di modificare esclusivamente la propria Password, non essendo registrati per quest’ultimo altri dati nel database. Per quanto riguarda l’utente, il tutto dipende dal tipo di registrazione che ha effettuato in precedenza : nel caso di registrazione “base” può eseguire le stesse azioni dell’amministratore, viceversa nel caso di registrazione “estesa”, essendo stati registrati anche i dati anagrafici, ha ovviamente la possibilità di modificarli. Un modo di utilizzo ulteriore disponibile soltanto all’utente, riguarda la possibilità di richiedere una mail contenente Username e Password di accesso in caso di dimenticanza. Modifica Username/Password <<extend>> Amministratore Utente Modifica Dati Anagrafici Richiesta Username/Password Figura 7 - Use Case Diagram Gestione Dati Personali 176 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.2 Class Diagram Mediante il Class Diagram è possibile definire tutte quelle che sono le entità caratteristiche del sistema software e le relazioni che ci sono tra di esse. Ciascuna entità è ovviamente modellabile attraverso il concetto di classe, che ne definisce le caratteristiche (i dati) ed il comportamento (le operazioni). Ovviamente, anche il Class Diagram può essere realizzato a diversi livelli di astrazione, passando da un livello elevato, definito di seguito, ad un livello più basso e strettamente legato alla fase di implementazione e quindi relazionato al framework ed al linguaggio utilizzato. Attraverso questo diagramma è possibile modellare la vista statica del sistema, tenendo anche conto di quelle che sono le funzionalità offerte da quest’ultimo e rese disponibili attraverso le entità che lo compongono. Sulla base delle specifiche definite per l’applicazione, il sistema prevede le seguenti entità e le relative classi che le modellano : - Ruolo : definisce un generico utilizzatore del sistema, da distinguere tra utente ed amministratore; - Utente : generico utente registrato che accede al sistema; - Amministratore : amministratore del sistema; - Accesso : classe astratta che contiene le informazioni relative all’accesso al sistema da parte di un utilizzatore qualsiasi; - AccessoUtente : informazioni di accesso di un utente; - AccessoAmministratore : informazioni di accesso dell’amministratore; - CartaCredito : contiene le informazioni della carta di credito con cui l’utente può acquistare una scheda prepagata; - SchedaPrepagata : scheda attraverso la quale l’utente può acquistare e scaricare i brani MP3; - Ricarica : singola operazione di ricarica della scheda; - BranoMP3 : contiene le informazioni relative ad un singolo brano MP3 presente nell’archivio; - FileMP3 : rappresenta il file MP3 fisico associato ad un certo brano; - Download : generica operazione di download di un brano; - Playlist : definisce una playlist creata da uno specifico utente; 177 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Il diagramma definito di seguito si pone ad un livello di astrazione intermedio, nell’ambito del quale i membri di ciascuna classe (campi e metodi), fanno riferimento al linguaggio utilizzato nell’implementazione, ossia il Java. Ovviamente, astraendosi da quest’ultimo, è facile osservare che la struttura del sistema che ne scaturisce è completamente indipendente dall’implementazione. In riferimento alle dipendenze che ci sono tra le varie classi, si possono fare le seguenti osservazioni : - le classi AccessoUtente ed AccessoAmministratore costituiscono una specializzazione della classe Accesso, più da un punto di vista concettuale che pratico; - la classe Ruolo generalizza le classi Utente ed Amministratore, in quanto queste ultime definiscono un particolare tipo di utilizzatore del sistema, l’uno diverso dall’altro ma con alcune caratteristiche comuni; - la classe Ricarica è di tipo associativo, in quanto l’Utente è legato alla SchedaPrepagata, non soltanto sulla base di un’operazione di acquisto ma anche di ricarica; - la classe Download è di tipo associativo, nell’ambito della relazione che c’è tra Utente e BranoMP3; - tra Playlist e BranoMP3 c’è un legame di tipo “aggregation”, tenendo conto che una playlist è costituita da uno o più brani, ma che comunque eliminando una playlist (il “tutto”) non elimino i brani dall’archivio (le “parti”), differentemente da quanto accade in una “composition”; Le altre tipologie di relazioni previste sono autoesplicative, in quanto esprimono il legame tra un’entità e le altre, sulla base delle specifiche e delle funzionalità del sistema. Infine, alle classi suddette sono associate, per ciascuna di esse, le corrispondenti classi per l’accesso alla base di dati per garantire la permanenza delle informazioni. 178 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore Accesso # # # # # # {abstract} # # # # # dataOraLogin dataOraLogout loginValido tentativo indirizzoIP : : : : : java.util.Date java.util.Date boolean byte java.lang.String <<Override>> <<Override>> <<Override>> <<Override>> <<Override>> <<Override>> # inserisci () : Accesso # aggiorna () : Accesso # leggi () : Accesso login () : Ruolo logout () : void modificaUserNamePassword () : Ruolo blocca () : void sblocca () : void controlloBlocco () : Ruolo 1..1 <<extends>> 0..* AccessoAmministratore <<extends>> <<extends>> # <<Override>> inserisci () : Accesso # <<Override>> aggiorna () : Accesso # <<Override>> leggi () : Accesso AccessoUtente CartaCredito # <<Override>> inserisci () : Accesso # <<Override>> aggiorna () : Accesso # <<Override>> leggi () : Accesso - 0..* tipo numero codiceSegreto dataScadenza : : : : java.lang.String java.lang.String java.lang.String java.util.Date Ruolo + verificaCopertura () : boolean 0..* 1..1 1..1 Utente + + + + + + # # # # # # nome cognome indirizzo citta provincia CAP stato telefono dataNascita sesso email dataOraRegistrazione acquistoMP3 <<Override>> <<Override>> <<Override>> <<Override>> <<Override>> <<Override>> : : : : : : : : : : : : : <<extends>> java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.lang.String java.util.Date char java.lang.String java.util.Date boolean registrazione () elimina () modificaDatiAnagrafici () visualizzaElencoUtenti () visualizzaDatiAnagrafici () richiediUserNamePassword () login () logout () modificaUserNamePassword () blocca () sblocca () controlloBlocco () # # # # userName password bloccato loggedIn : : : : java.lang.String java.lang.String boolean boolean # # # # # # login () : Ruolo logout () : void modificaUserNamePassword () : Ruolo blocca () : void sblocca () : void controlloBlocco () : Ruolo SchedaPrepagata - importoResiduo : float - dataOraAcquisto : java.util.Date - dataScadenza : java.util.Date 1..1 0..1 Ricarica : : : : : : : : : : : : Utente void Utente java.util.List Utente void Ruolo void Ruolo void void Ruolo - importo : float - dataOra : java.util.Date 1..1 + + + + + + + ricarica () acquista () elimina () visualizzaInfo () controlloScadenza () controlloImportoResiduo () aggiornaImportoResiduo () : : : : : : : SchedaPrepagata SchedaPrepagata void SchedaPrepagata boolean boolean SchedaPrepagata + esegui () : Ricarica 0..* Download - dataOrtaAcquisto : java.util.Date + inserisci () : Download 0..* Playlist 0..* - nome : java.lang.String - dataOraCreazione : java.util.Date BranoMP3 - genere titolo autore album durata costo dimensione nomeFileMP3 : : : : : : : : java.lang.String java.lang.String java.lang.String java.lang.String java.sql.Time float long java.lang.String + + + + + + + + + inserisci () : elimina () : ascolta () : acquista () : modificaInfo () : visualizzaInfo () : ricerca () : visualizzaElencoBraniMP3 () : avviaDownload () : BranoMP3 void FileMP3 BranoMP3 BranoMP3 BranoMP3 java.util.List java.util.List void + + + + + + + + 0..* 0..* inserisciBrano () : eliminaBrano () : ascolta () : elimina () : crea () : visualizzaElencoPlaylist () : visualizzaElencoBraniMP3Playlist () : visualizzaInfo () : void void FileMP3 void Playlist java.util.List java.util.List Playlist FileMP3 1..1 1..1 - nomeFile filePath dimensione tagTitolo tagAutore tagAlbum : : : : : : java.lang.String java.lang.String long java.lang.String java.lang.String java.lang.String + + + + + + leggiT ag () : T ag aggiornaT ag () : FileMP3 riproduci () : void upload () : FileMP3 download () : FileMP3 elimina () : void Figura 8 - Class Diagram 179 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3 Activity Diagrams – Sequence Diagrams Facendo riferimento a quelle che sono le funzionalità offerte dal sistema e le modalità di utilizzo di quest’ultimo, attraverso gli Activity Diagrams è possibile modellare le azioni necessarie per compiere un’attività ed il flusso di controllo tra di esse. Ciascuna “activity” rappresenta un’azione che viene eseguita dal sistema oppure dall’utilizzatore e può essere atomica oppure scomposta in più “action” elementari. Nella gestione del flusso di controllo, è possibile definire esecuzioni parallele di operazioni e la loro sincronizzazione, nonché esecuzioni condizionali. Inoltre, attraverso lo strumento dell’ “object flow” è possibile specificare quali sono gli oggetti del sistema che entrano in gioco per ciascuna azione eseguita e quale sia il loro stato. Invece, attraverso i Sequence Diagrams è possibile descrivere la vista dinamica del sistema, enfatizzando le interazioni dovute allo scambio di messaggi tra gli oggetti e il loro ordinamento temporale. Ciascun diagramma evidenzia il modo in cui uno scenario, ossia uno specifico percorso in un caso d’uso, viene risolto dalla collaborazione tra un insieme di oggetti. Generalmente, però, al posto di realizzare un diagramma per ciascuno scenario, si preferisce definire un unico diagramma all’interno del quale vengono descritte le situazioni alternative, facendo uso dei numeri di sequenza sui messaggi. E’ da osservare che nell’ambito dei Sequence Diagrams sono previsti degli oggetti di tipo “Interfaccia”, che definiscono le parti del sistema che interagiscono con l’utente, ossia la cosiddetta UI (User Interface). In questa fase, non viene specificata la tipologia di interfaccia utente, in modo da poter sfruttare i diagrammi realizzati anche con implementazioni diverse. E’ ovvio che, nel caso di una Web application, l’interfaccia utente sia caratterizzata da pagine Web e dai relativi form che le compongono. E’ stato definito un Activity Diagram di carattere generale, che si pone ad un elevato livello di astrazione e che descrive tutte le possibili azioni ed il relativo flusso, che possono essere eseguite da un utente non registrato, da un utente registrato ed ovviamente dall’amministratore. 180 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ [Utente registrato - password dimenticata] [Utente non registrato] Registrazione Richiedi Password [Utente registrato oppure Amministratore - login] Inserimento Username e Password [Non prevede acquisto/download di brani MP3] [Prevede acquisto/download di brani MP3] Acquisto Scheda Prepagata [1° errore - Password errata oppure Ruolo già loggato] [Username e Password corretti] Verifica copertura Carta di Credito [2° errore - Password errata] Utente/Amministratore bloccato [Carta di Credito scoperta] [Carta di Credito coperta] Login Sistema Richiesta Servizio Attività comuni a Utente e Amministratore Gestione Dati Personali [Amministratore] [Utente] Ascolto brani MP3 Logout Sistema Visualizzazione/Ricerca brani MP3 Attività distinte fra Utente e Amministratore Gestione Scheda Prepagata Acquisto/Download brani MP3 Gestione Archivio brani MP3 Gestione Playlist Gestione Utenti Figura 9 - Activity Diagram Generale A partire da esso, è possibile scendere ad un livello di dettaglio maggiore, specificando la sequenza delle azioni per ogni funzionalità del sistema. 181 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.1 Login Per quanto riguarda il Login dell’utente e dell’amministratore, le azioni da eseguire sono praticamente le stesse a meno degli oggetti che entrano in gioco. Una volta inseriti Username e Password, il sistema ne effettua il controllo della correttezza, valutando in primo luogo se lo Username esiste e successivamente verificando la validità della Password. Ovviamente, a colui che tenta di accedere, viene sempre dato un messaggio generico di errore, senza mai specificare quale dei due parametri sia errato. Per evitare tentativi di accesso continuamente errati, magari eseguiti non effettivamente dalla persona titolare dell’account, il sistema blocca l’utilizzatore dopo due tentativi di accesso consecutivi errati. Tale blocco può essere rimosso esclusivamente contattando l’amministratore del sistema, che ha i diritti di accesso alla gestione degli utenti. Ovviamente, nel caso in cui Username e Password siano corretti, si valuta comunque se è attivo un blocco causato da tentativi errati precedenti ; nel caso di login consentito, si esegue l’aggiornamento delle informazioni di accesso. La monitorizzazione e registrazione degli accessi, validi o non validi, relativi agli utilizzatori della Web application, può essere considerato uno strumento mediante il quale sia possibile eseguire delle statistiche o comunque ricercare eventuali frodi e tentativi di accesso non legittimi al sistema. Nell’ambito del Sequence Diagram, è possibile osservare lo scenario di “Username errata” e quello di “Username corretta”, nell’ambito del quale si possono verificare le seguenti situazioni distinte : - Password errata al 1° tentativo; - Password errata al 2° tentativo che comporta il blocco dell’utilizzatore; - Password corretta ma l’utilizzatore è bloccato; - Utilizzatore già loggato; - Login consentito; 182 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Sistema Inserimento Username e Password :Utente [letto] : 1 Controllo Username e Password [Username corretta 1° tentativo] [Username errata] [Username corretta 2° tentativo] :AccessoUtente [creato] [1° errore - Password errata oppure Utente già loggato] [Username e Password corretti] :Utente [letto] : 2 Controllo blocco [Utente bloccato] [Utente non bloccato] [2° errore - Password errata] Login Sistema :AccessoUtente [aggiornato] Visualizzazione Menu Utente Utente/Amministratore bloccato Aggiornamento del numero di tentativi errati di accesso :Utente [bloccato] Figura 10 - Activity Diagram Login Utente 183 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore Sistema Inserimento Username e Password :Amministratore [letto] : 1 Controllo Username e Password [Username corretta 1° tentativo] [Username errata] [Username corretta 2° tentativo] :AccessoAmministratore [creato] [1° errore - Password errata oppure Amministratore già loggato] [Username e Password corretti] :Amministratore [letto] : 2 Controllo blocco [Amministratore bloccato] [Amministratore non bloccato] [2° errore - Password errata] Login Sistema :AccessoAmministratore [aggiornato] Visualizzazione Menu Amministratore Utente/Amministratore bloccato Aggiornamento del numero di tentativi errati di accesso :Amministratore [bloccato] Figura 11 - Activity Diagram Login Amministratore 184 Utente 2: create 10.4.1: noti fi ca Utente gi à l oggato 10.3.3.1.1: utente bl occato : i m pedi to l 'accesso 10.2.5: noti fi ca bl occo utente 10.1.1: segnal azi one errore 9.1.1: segnal azi one errore Scenari o 1 : Usernam e errata 5: esegui l ogi n 4: i nseri m ento Usernam e e Password 3: ri chi esta i nseri m ento Usernam e e Password 1: ri chi esta di l ogi n Interfacci a Hom e Scenari o 2 : Usernam e corretta 9.1: err: Usernam e errata 7: l ogi n( ) 6: create :Utente 17: destroy 16: i nseri m ento esegui to Sequenza azi oni com uni a tutti gl i scenari , per l 'aggi ornam ento del l e i nform azi oni di accesso 8: l eggi ( ) :AccessoUtente 9: cari ca dati 10.3.3.2.1: create 10.3.3: cari ca dati 10.3.2: l eggi ( ) 10.2.3: aggi ornam ento esegui to 10.2.2: scri vi ( ) 9.2.1.1: i ni zi al i zzazi one esegui ta 9.2.1: create 15: destroy 14: i nseri m ento esegui to 11: i nseri sci ( ) 10.3.3.2.2: vi sual i zzazi one m enu Utente 10.3.3.2: l ogi n consenti to 10.4: err: Utente gi à l oggato 10.3.3.1: err: Utente bl occato 10.3.1: control l oBl occo( ) 10.3: Password corretta 10.2.4: bl occo esegui to 10.2.1: bl occa( ) 10.2: err: Password errata al 2° tentati vo 10.1: err: Password errata al 1° tentati vo Interfacci a Logi n AccessoDB 13: i nseri m ento esegui to 12: i nseri sci ( ) Lo creo sol o i n caso di scenari o "Usernam e corretta - 1° tentati vo". Se sono al 2° tentati vo, gi à l 'ho creato a quel l o precedente. UtenteDB Interfacci a M enu Utente Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 12 - Sequence Diagram Login Utente 185 Interfacci a Logi n 10.4.1: noti fi ca Am m i ni stratore gi à l oggato 10.3.3.1.1: bl occato : i m pedi to l 'accesso 10.2.5: noti fi ca bl occo am m i ni stratore 10.1.1: segnal azi one errore 9.1.1: segnal azi one errore Scenari o 1 : Usernam e errata 5: esegui l ogi n 4: i nseri m ento Usernam e e Password 3: ri chi esta i nseri m ento Usernam e e Password 2: create Interfacci a Hom e 1: ri chi esta di l ogi n Am m i ni stratore 16: i nseri m ento esegui to Sequenza azi oni com uni a tutti gl i scenari , per l 'aggi ornam ento del l e i nform azi oni di accesso 17: destroy 9.2.1: create :AccessoAm m i ni stratore 9: cari ca dati 8: l eggi ( ) 10.3.3.2.1: create 10.3.3: cari ca dati 10.3.2: l eggi ( ) 10.2.3: aggi ornam ento esegui to 10.2.2: scri vi ( ) 9.2.1.1: i ni zi al i zzazi one esegui ta :Am m i ni stratore 15: destroy 14: i nseri m ento esegui to 11: i nseri sci ( ) 10.3.3.2.2: vi sual i zzazi one m enu Am m i ni stratore 10.3.3.2: l ogi n consenti to 10.4: err: Am m i ni stratore gi à l oggato 10.3.3.1: err: Am m i ni stratore bl occato 10.3.1: control l oBl occo( ) 10.3: Password corretta 10.2.4: bl occo esegui to 10.2.1: bl occa( ) 10.2: err: Password errata al 2° tentati vo 10.1: err: Password errata al 1° tentati vo Scenari o 2 : Usernam e corretta 9.1: err: Usernam e errata 7: l ogi n( ) 6: create 13: i nseri m ento esegui to 12: i nseri sci ( ) Lo creo sol o i n caso di scenari o "Usernam e corretta - 1° tentati vo". Se sono al 2° tentati vo, gi à l 'ho creato a quel l o precedente. Am m i ni stratoreDB AccessoDB Interfacci a M enu Am m i ni stratore Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 13 - Sequence Diagram Login Amministratore 186 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.2 Logout Anche la funzionalità Logout prevede le medesime azioni per l’utente e l’amministratore, a meno degli oggetti che vengono utilizzati per le operazioni necessarie. Utente Sistema Registrazione informazioni di logout Richiesta di Logout :AccessoUtente [aggiornato] Inserisce data ed ora di logout Figura 14 - Activity Diagram Logout Utente Amministratore Richiesta di Logout Sistema Registrazione informazioni di logout :AccessoAmministratore [aggiornato] Inserisce data ed ora di logout Figura 15 - Activity Diagram Logout Amministratore 187 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Interfaccia Home :Utente :AccessoUtente AccessoDB UtenteDB Utente 1: richiesta di logout 2: logout( ) 3: aggiorna( ) 4: modifica( ) 5: aggiornamento eseguito 6: aggiornamento eseguito 7: destroy 8: modifica() 9: aggiornamento eseguito 10: notifica logout 11: destroy 12: visualizzazione home Figura 16 - Sequence Diagram Logout Utente Interfaccia Home :Amministratore :AccessoAmministratore AccessoDB AmministratoreDB Amministratore 1: richiesta di logout 2: logout( ) 3: aggiorna( ) 4: modifica( ) 5: aggiornamento eseguito 6: aggiornamento eseguito 7: destroy 8: modifica() 9: aggiornamento eseguito 10: notifica logout 11: destroy 12: visualizzazione home Figura 17 - Sequence Diagram Logout Amministratore 188 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.3 Registrazione Per quanto riguarda la registrazione dell’utente, per poter usufruire dei servizi della Web application, ne sono previste due tipologie : la versione “base” ed “estesa”, che differiscono dal fatto che quest’ultima fornisce in più la possibilità di acquisto e download dei brani MP3. La registrazione di base prevede l’immissione da parte dell’utente, soltanto dei dati di accesso, Username e Password, oltre che dell’indirizzo email. In questo modo, si ha la possibilità di usufruire dei servizi in maniera del tutto gratuita, con la limitazione di poter esclusivamente ascoltare i brani in streaming, senza la possibilità di download. L’utente ha poi la possibilità di decidere se voler usufruire della funzionalità di acquisto e download dei brani, nel qual caso deve immettere anche i dati anagrafici. Oltre a questi ultimi, sono necessarie le informazioni relative alla scheda prepagata da acquistare e della carta di credito da utilizzare per il pagamento, della quale il sistema esegue l’operazione di verifica della copertura. Ovviamente, sono previste tutte le operazioni di validazione dei dati, necessarie a garantire che i dati immessi dall’utente rispettino i formati attesi dall’applicazione. Al termine della registrazione, viene eseguito il login automatico al sistema, visualizzando all’utente le voci di menù relative alle funzionalità abilitate. Per quanto riguarda il Sequence Diagram, si evidenziano i due possibili scenari relativi alla registrazione “base” ed “estesa”, oltre alla funzionalità di login automatico, che non prevede tutti i controlli tipici di questa operazione, trattandosi di un nuovo utente. Il diagramma, inoltre, ha un riferimento al Sequence Diagram che riguarda l’acquisto della scheda prepagata, in quanto questa operazione può essere eseguita dall’utente anche in un secondo momento e non necessariamente in fase di registrazione. Per questo motivo è prevista una funzionalità a parte con gli Activity e Sequence Diagrams corrispondenti. 189 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Sistema Controllo correttezza dati Inserimento dati Anagrafici [Dati non corretti] [Dati corretti] Registrazione dati Anagrafici :Utente [creato] [Prevede acquisto/download di brani MP3] Inserimento dati Carta di Credito ed Importo Scheda Controllo correttezza dati carta :CartaCredito [creato] [Non prevede acquisto/download di brani MP3] Verifica copertura Carta di Credito [Carta di Credito scoperta] [Carta di Credito coperta] Registrazione Acquisto :SchedaPrepagata [acquistata] Login Automatico Visualizzazione Menu Utente :AccessoUtente [creato] :Utente [registrato] Figura 18 - Activity Diagram Registrazione 190 2: create 11: notifica registrazione eseguita :Utente 19: destroy 18: inserimento eseguito Esecuzione del Login in automatico 10: registrazione eseguita 7: registrazione( ) UtenteDB 20: create 17: destroy 16: inserimento eseguito 13: inserisci( ) 12: create 9: inserimento eseguito 8: inserisci( ) 6.2.1.2: esegui acquisto scheda Interfaccia AcquistoRicarica Scheda 21: visualizzazione menu Utente 6.2.1.4: destroy Sequence Diagram Acquisto Scheda Prepagata 6.2.1.1: create 6.2: create 6: controllo correttezza dati Interfaccia Registrazione 6.2.1.3: notifica acquisto eseguito Scenario : l'Utente prevede di acquistare MP3 6.1: err: dati non corretti 5: esegui registrazione 4: inserimento dati 3: richiesta inserimento dati Utente 1: richiesta di registrazione Interfaccia Home 14: inserisci( ) 15: inserimento eseguito :AccessoUtente AccessoDB Interfaccia Menu Utente Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 19 - Sequence Diagram Registrazione 191 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.4 Richiesta Username / Password Nel caso in cui un utente registrato abbia perso oppure dimenticato le informazioni relative all’accesso, è possibile utilizzare la funzionalità del sistema che permette di inviare una mail contenente tali informazioni. L’utente deve semplicemente fornire al sistema l’indirizzo email con cui ha effettuato la registrazione, dopodichè il sistema stesso eseguo il controllo di correttezza del formato dell’indirizzo e verifica che quest’ultimo sia correttamente registrato. In caso di esito positivo, l’utente riceverà una mail con tutte le informazioni richieste. Utente Sistema Controllo correttezza dati Inserimento indirizzo Email [Dati non corretti] [Dati corretti] Controllo indirizzo Email [Indirizzo email non presente in archivio] :Utente [letto] [Indirizzo email presente in archivio] Invio Email con Username e Password Figura 20 - Activity Diagram Richiesta Username/Password 192 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Interfaccia Home UtenteDB Utente 1: richiesta Username e Password 2: create Interfaccia Richiesta Username/Password 3: richiesta inserimento email 4: inserimento email 5: esegui richiesta 6: controllo formato email 6.1: err: formato email non valido 6.2.1: create :Utente 6.2.2: richiediUserNamePassword( ) 6.2.3: leggi( ) 6.2.4: carica dati 6.2.4.1.1: utente non registrato 6.2.4.1.2: err: indirizzo email non presente in archivio 6.2.4.2.1: invio mail 6.2.4.2.2: invio email eseguito 6.2.4.2.3: notifica invio mail 6.2.5: destroy Figura 21 - Sequence Diagram Richiesta Username/Password 3.3.5 Acquisto, Ricarica e Visualizzazione Info Scheda Prepagata L’acquisto di una scheda prepagata si rende necessario nel momento in cui l’utente abbia intenzione di acquistare e scaricare brani MP3. L’operazione di acquisto può essere eseguita in fase di registrazione oppure in un secondo momento, quando l’utente ha già effettuato in passato la registrazione base. Le informazioni necessarie al sistema per svolgere la funzionalità sono relative all’importo della scheda ed alla carta di credito, mediante la quale eseguire il pagamento. Una volta verificata ed accertata la copertura di quest’ultima, viene completato l’acquisto della scheda. 193 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Sistema Controllo correttezza dati Inserimento dati Carta di Credito ed Importo Scheda [Dati non corretti] [Dati corretti] :CartaCredito [creato] Verifica copertura Carta di Credito [Carta di Credito scoperta] [Carta di Credito coperta] Registrazione Acquisto :SchedaPrepagata [acquistata] Visualizzazione Menu Utente Figura 22 - Activity Diagram Acquisto scheda prepagata 194 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Interfaccia AcquistoRicarica Scheda SchedaPrepagataDB Utente 1: richiesta inserimento dati 2: inserimento dati 3: esegui acquisto scheda 4: controllo correttezza dati 4.1: err: dati non corretti 4.2.1: create :CartaCredito 4.2.2: verificaCopertura( ) 4.2.2.1.1: err: carta di credito scoperta 4.2.2.1.2: err: carta di credito scoperta 4.2.2.2.1: carta di credito coperta 4.2.2.2.2: destroy 4.2.2.2.3: create :SchedaPrepagata 4.2.2.2.4: acquista( ) 4.2.2.2.5: inserisci( ) 4.2.2.2.6: acquisto eseguito 4.2.2.2.7: acquisto eseguito 4.2.2.2.8: destroy 4.2.2.2.9: notifica acquisto eseguito Figura 23 - Sequence Diagram Acquisto scheda prepagata La scheda prepagata viene utilizzata per l’acquisto ed il download dei brani MP3 disponibili nell’archivio, per cui l’importo residuo è destinato a diminuire nel tempo. Il sistema fornisce una funzionalità di ricarica, mediante la quale l’utente ha la possibilità di ricaricare la scheda aumentandone l’importo. L’operazione di ricarica è del tutto simile a quella di acquisto, nel senso che le informazioni necessarie riguardano l’importo della ricarica e la carta di credito necessaria al pagamento. Una volta eseguiti i dovuti controlli di copertura di quest’ultima, la ricarica viene eseguita e registrata dal sistema ed inoltre, viene aggiornato l’importo residuo della scheda, nonché prolungata la data di scadenza. 195 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Sistema Controllo correttezza dati Inserimento Dati Carta di Credito ed Importo Ricarica [Dati non corretti] [Dati corretti] :CartaCredito [creato] Verifica copertura Carta di Credito [Carta di Credito scoperta] [Carta di Credito coperta] :Ricarica [eseguita] Aggiornamento importo, scadenza e data/ora Esecuzione Ricarica :SchedaPrepagata [ricaricata] Figura 24 - Activity Diagram Ricarica scheda prepagata 196 4.1: err: dati non corretti 3: esegui ricarica scheda 2: inserimento dati 1: richiesta inserimento dati 4.2.2.2.16: notifica ricarica eseguita 4.2.2.1.2: err: carta di credito scoperta Utente :CartaCredito 4.2.2.2.15: destroy 4.2.2.2.14: ricarica eseguita 4.2.2.2.4: ricarica( ) 4.2.2.2.3: create 4.2.2.2.2: destroy 4.2.2.2.1: carta di credito coperta 4.2.2.1.1: err: carta di credito scoperta 4.2.2: verificaCopertura( ) 4.2.1: create 4: controllo correttezza dati Interfaccia AcquistoRicarica Scheda 4.2.2.2.10: destroy 4.2.2.2.9: ricarica eseguita 4.2.2.2.6: esegui( ) 4.2.2.2.5: create 4.2.2.2.7: inserisci( ) 4.2.2.2.12: modifica( ) 4.2.2.2.8: inserimento eseguito :Ricarica 4.2.2.2.13: aggiornamento importo residuo eseguito 4.2.2.2.11: aggiornaImportoResiduo( ) :SchedaPrepagata RicaricaDB SchedaPrepagataDB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 25 - Sequence Diagram Ricarica scheda prepagata 197 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ L’applicazione prevede di visualizzare tra le voci di menu, le informazioni relative ad un eventuale scheda prepagata acquistata dall’utente, senza che quest’ultimo attivi una particolare funzionalità del sistema. Tali informazioni riguardano l’importo residuo e la data di scadenza ed un eventuale messaggio di avvertimento per l’utente, qualora la scheda sia scaduta. Utente Sistema Caricamento dati Richiesta visualizzazione informazioni :SchedaPrepagata [letto] Visualizzazione informazioni Scheda Figura 26 - Activity Diagram Visualizzazione info scheda prepagata Interfaccia Home Utente SchedaPrepagataDB 1: richiesta informazioni scheda 2: create Interfaccia Info Scheda 3: create :SchedaPrepagata 4: visualizzaInfo( ) 5: leggi( ) 6: carica dati 7: controlloScadenza( ) 8: informazioni scheda 9: destroy 10: visualizzazione informazioni scheda Figura 27 - Sequence Diagram Visualizzazione info scheda prepagata 198 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.6 Visualizzazione e Ricerca Brani Per poter accedere all’archivio dei brani e visualizzarne il contenuto, il sistema mette a disposizioni due modalità : - visualizzazione dei brani sulla base di un genere musicale; - ricerca dei brani sulla base dei criteri impostati relativi al titolo, autore, album e genere musicale; Gli Activity Diagrams che descrivono queste funzionalità sono due, in quanto fanno riferimento alla richiesta da parte dell’utilizzatore ed alle operazioni eseguite dal sistema. Si è preferita una scomposizione in questi termini, poiché tali diagrammi diventano componenti dei diagrammi che descrivono funzionalità più complesse che prevedono la visualizzazione dei brani. In questo modo si riesce a modellare il sistema mediante livelli di astrazione successivi. Il Sequence Diagram, invece, evidenzia i due possibili scenari sulla base dei quali l’utilizzatore può richiedere la visualizzazione dei brani presenti all’interno dell’archivio. 199 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Selezione modalità visualizzazione brani MP3 Richiesta ricerca Brano Richiesta visualizzazione brani MP3 per genere Inserimento criteri di ricerca Figura 28 - Activity Diagram Richiesta Visualizzazione e ricerca brani MP3 [Richiesta ricerca] [Richiesta visualizzazione per genere] Ricerca brani MP3 Caricamento elenco brani MP3 :BranoMP3 [letto] Visualizzazione brani MP3 Figura 29 - Activity Diagram Visualizzazione e ricerca brani MP3 200 1.1.3: create Scenario 1 : Visualizzazione Brani MP3 per genere 1.2.1: create Interfaccia Ricerca MP3 1.2.8.2.2: visualizzazioni brani relativi ai criteri di ricerca impostati 1.2.8.1.2: notifica assenza brani per i criteri di ricerca impostati 1.2.4: esegui ricerca 1.2.3: inserimento criteri di ricerca 1.2.2: richiesta inserimento criteri di ricerca 1.2: richiesta ricerca brani MP3 1.2.5: create 1.2.9: destroy 1.2.8.2.1: brani caricati 1.2.8.1.1: err: assenza brani 1.2.6: ricerca( ) 1.1.8: destroy 1.1.7.2.1: brani caricati 1.1.7.1.1: err: assenza brani 1.1.6: leggi( ) BranoMP3DB 1.2.8: carica dati 1.2.7: leggi( ) 1.1.7: carica dati :BranoMP3 1.1.5: visualizzaElencoBraniMP3( ) 1.1.4: create Interfaccia Elenco Brani MP3 Scenario 2 : Visualizzazione Brani MP3 sulla base di una ricerca personalizzata 1.1.7.2.2: visualizzazione brani del genere selezionato 1.1.7.1.2: notifica assenza brani per il genere selezionato 1.1.2: esegui visualizzazione 1.1.1: selezione genere Utente/Amministratore 1.1: richiesta visualizzazione elenco brani per genere Interfaccia Home Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 30 - Sequence Diagram Visualizzazione e ricerca Brani MP3 201 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.7 Inserimento, Modifica ed Eliminazione Brano dall’Archivio Nell’ambito della gestione dell’archivio dei brani, l’amministratore ha ovviamente la possibilità di inserire un nuovo brano MP3. Le informazioni richieste dal sistema riguardano il titolo, l’autore, l’album, il genere musicale, la durata oltre al costo, necessario per l’acquisto del brano, ed al file MP3 che bisogna caricare sul server. Le informazioni suddette non sono tutte strettamente necessarie, se non il genere ed il costo. La durata può anche non essere impostata, così come le informazioni caratteristiche del brano. Una scelta di questo tipo potrebbe portare a pensare ad un grave errore di progettazione, permettendo il caricamento di brani senza specificarne il titolo, l’autore e l’album di appartenenza. In realtà, il sistema prevede un meccanismo di accesso ai cosiddetti tag ID3v1, caratteristici dei file in formato MP3 (MPLEG Layer 3), che contengono tutte le informazioni caratteristiche del brano, tra cui appunto il titolo, l’autore e l’album. In questo modo, si da all’amministratore la possibilità di non specificare tali informazioni in fase di inserimento, se queste ultime possono essere ricavate dai tag stessi. Nel caso in cui anche questi siano vuoti, si segnala all’utente che le informazioni sono necessarie. E’ ovvio che viene garantita sempre la coerenza tra le informazioni del brano inserite nell’archivio ed i tag del file MP3 corrispondente, per cui se le informazioni del brano sono specificate dall’amministratore, il sistema provvede ad aggiornarne i valori nei corrispondenti tag. Questo meccanismo permette di risparmiare una grande quantità di tempo, se si dispone di un gruppo di brani, le cui informazioni principali sono già specificate attraverso i tag ID3v1. Il Sequence Diagram evidenzia le possibili situazioni di caricamento del singolo brano, attraverso due scenari distinti. 202 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore Sistema Controllo correttezza dati Inserimento informazioni brano e file MP3 [Dati non corretti] [Dati corretti] :FileMP3 [upload] Controllo informazioni brano MP3 e upload file [Informazioni brano inserite] Scrivi informazioni nei tag MP3 [Informazioni brano non inserite] Ricava informazioni dai tag MP3 :FileMP3 [aggiornato] :FileMP3 [letto] Registrazione dati in archivio :BranoMP3 [creato] Figura 31 - Activity Diagram Inserimento brano MP3 in archivio 203 2: create 6.2.11: notifica inserimento eseguito In base al fatto che l'Amministratore abbia inserito o meno le informazioni del brano, queste vengono scritte o ricavate nei/dai tag MP3 6.1: err: dati non validi 5: esegui inserimento brano 4: inserimento informazioni e selezione brano da caricare 6.2.10: inserimento eseguito 6.2.7: inserisci( ) 6.2.6: create 6.2.5: destroy 6.2.4.2.2: informazioni brano scritte nei tag MP3 6.2.4.2.1: aggiornaTag( ) 6.2.4.1.2: informazioni brano ricavate dai tag MP3 6.2.4.1.1: leggiTag( ) 6.2.4: verifica informazioni brano 6.2.3: termine upload 6.2.2: upload( ) 6.2.1: create 6: controllo correttezza dati Interfaccia Inserimento Brano MP3 3: richiesta inserimento informazioni e selezione brano Amministratore 1: richiesta inserimento brano MP3 Interfaccia Home :FileMP3 6.2.8: inserisci( ) 6.2.9: inserimento eseguito :BranoMP3 BranoMP3DB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 32 - Sequence Diagram Inserimento brano MP3 in archivio 204 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Una volta che un brano è stato inserito, è ovviamente disponibile una funzionalità mediante la quale l’amministratore può visualizzare e modificare le informazioni dello stesso. Così come per l’inserimento, viene sempre garantita la coerenza tra le informazioni memorizzate nella base di dati ed i tag ID3v1 del file MP3, i quali vengono aggiornati nel caso in cui l’amministratore decidesse di modificare il titolo, l’autore e l’album. La visualizzazione dei dati prevede un Activity Diagram che diventa componente in quello di modifica, considerando che la funzionalità di visualizzazione è utilizzata in molteplici situazioni. Caricamento dati brano MP3 :BranoMP3 [letto] Visualizzazione dati MP3 Figura 33 - Activity Diagram Visualizzazione info brano MP3 Inoltre, l’Activity Diagram relativo alla funzionalità di modifica sfrutta i diagrammi di visualizzazione e ricerca dei brani, tra i quali selezionare il brano di cui modificarne le informazioni. 205 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore Sistema Richiesta visualizzazione brani MP3 Visualizzazione elenco brani MP3 [Richiesta [Richiesta visualizzazione per ricerca] genere] Selez ione modalità vis ualizz azione brani MP3 Ricerca brani MP 3 Caricamento elenco brani MP3 :BranoMP 3 [letto] Ric hiesta ricerca Bran Ric hiesta v isualiz zaz ione brani MP3 per gene Visualizzazione brani MP 3 Inserimento c riteri di ricerc a [Assenza brani] [Presenza brani] Visualizzazione dati brano MP3 Selezione brano MP3 Caricamento dati brano MP3 :BranoMP3 [letto] Vis ualizz azione dati MP3 Inserimento nuovi dati Controllo correttezza dati [Dati non corretti] :FileMP3 [aggiornato] [Dati corretti] Registrazione nuovi dati :BranoMP3 [aggiornato] Figura 34 - Activity Diagram Modifica brano MP3 206 2.2: visualizzazione elenco brani 2.1: err: assenza brani 2: create 2.2.9: inserimento nuove informazioni del brano 2.2.11.2.10: notifica aggiornamento eseguito 2.2.11.1: err: dati non validi 2.2.10: esegui aggiornamento informazioni brano 2.2.2: create Interfaccia Elenco Brani MP3 2.2.8: visualizzazione informazioni brano 2.2.1: selezione brano da modificare Sequence Diagram Visualizzazione Brani MP3 per Genere o Ricerca personalizzata Amministratore 1: richiesta visualizzazione brani Interfaccia Home 2.2.11.2.9: destroy 2.2.11.2.2: modifica( ) 2.2.6: carica dati 2.2.5: leggi( ) 2.2.11.2.7: destroy 2.2.11.2.6: aggiornamento eseguito 2.2.11.2.5: aggiornaTag( ) 2.2.11.2.4: create 2.2.11.2.3: aggiornamento eseguito :BranoMP3 2.2.11.2.8: aggiornamento eseguito 2.2.11.2.1: modificaInfo( ) 2.2.11: controllo correttezza dati 2.2.7: informazioni brano 2.2.4: visualizzaInfo( ) 2.2.3: create Interfaccia Info Brano MP3 BranoMP3DB :FileMP3 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 35 - Sequence Diagram Modifica brano MP3 207 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ L’ultima funzionalità fondamentale per la gestione dell’archivio permette l’eliminazione di un brano MP3. Tale operazione è prevista secondo due possibili modalità : - eliminazione di uno o più brani selezionandoli dall’elenco attualmente visualizzato; - eliminazione di un singolo brano nell’interfaccia di visualizzazione delle informazioni dello stesso; Tali modalità sono descritte attraverso i due corrispondenti scenari all’interno del Sequence Diagram Nel momento in cui l’amministratore richiede l’eliminazione di uno o più brani, per garantire la coerenza delle informazioni in archivio e dei file MP3 presenti nel file system del server, il sistema esegue le seguenti attività su ciascun di essi : - cancella le informazioni del brano dalla base di dati; - elimina il file MP3 ad esso associato; - aggiorna le informazioni di composizione delle playlist degli utenti; L’ultima operazione non è strettamente legata all’azione di cancellazione del brano, ma serve ad evitare che all’interno delle playlist degli utenti rimanga un riferimento ad un brano non più presente in archivio e per il quale non è disponibile il file MP3 da ascoltare. 208 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore Richiesta visualizzazione brani MP3 Sistema Visualizzazione elenco brani MP3 [Richiesta ricerca] [Richiesta visualizzazione per genere] Selez ione modalità vis ualizz az ione brani MP3 Ricerca brani MP3 Caricamento elenco brani MP 3 :B ranoMP 3 [letto] Ric hiesta ric erca Bran Ric hies ta v isualiz zazione brani MP3 per gene V isualizzazione brani MP 3 Inserimento c riteri di ric erca [Assenza brani] [Presenza brani] Visualizzazione dati brano MP3 Caric amento dati brano MP3 Selezione brano MP3 :BranoMP3 [letto] Selezione dei brani MP3 da cancellare Vis ualizz az ione dati MP3 Richiesta cancellazione brano/i MP3 Cancellazione brano MP3 :BranoMP3 [cancellato] :FileMP3 [cancellato] :Playlist [aggiornato] Figura 36 - Activity Diagram Eliminazione brano MP3 dall'archivio 209 2.2.2.2.20: noti fi ca el i m inazi one eseguita 2.2.2.2.9: ri chi esta cancell azione brano 2.2.2.2.19: destroy 2.2.3: destroy 2.2.4: destroy 2.2.2.2.17: el i m inazi one esegui ta 2.2.2.2.10: el i m ina( ) :Fil eM P3 BranoM P3DB 2.2.2.2.13: el im i na( ) 2.2.2.2.12: el i mi nazi one esegui ta 2.2.2.2.11: el im i na( ) 2.2.2.2.6: cari ca dati 2.2.2.2.5: l eggi ( ) 2.2.2.1.9: el i m inazi one esegui ta 2.2.2.1.8: el i m ina( ) 2.2.2.1.7: el i m inazi one eseguita 2.2.2.1.6: el i m ina( ) 2.2.2.1.5: el i m inazi one eseguita 2.2.2.1.4: el i m ina( ) 2.2.2: create 2.2.2.2.16: el im i nazione esegui ta 2.2.2.2.15: el im i na( ) 2.2.2.2.14: el i mi nazi one esegui ta :BranoM P3 2.2.2.2.7: i nform azi oni brano 2.2.2.2.4: vi suali zzaInfo( ) Interfaccia Info Brano M P3 2.2.2.1.10: el im i nazione esegui ta 2.2.2.1.3: el i m ina( ) 2.2.1: create 2.2.2.2.18: el im i nazione esegui ta 2.2.2.2.3: create Interfacci a Elenco Brani M P3 2.2.2.2.8: vi sual i zzazi one i nform azi oni brano Scenari o 2 : el i m inazi one di brani sel ezi onandone uno e vi suali zzandone le i nform azi oni 2.2.2.2.2: richiesta vi sual i zzazi one info brano 2.2.2.2.1: sel ezi one brano M P3 2.2.2.1.11: noti fi ca el i m inazi one eseguita Scenari o 1 : eli m i nazi one di brani selezionandol i dal l'el enco 2.2.2.1.2: esegui cancell azione 2.2.2.1.1: sel ezi one brano/i da cancel lare 2.2: vi sual i zzazi one el enco brani 2.1: err: assenza brani Sequence Di agram Visual izzazi one Brani M P3 per Genere o Ri cerca personal i zzata 2: create Interfaccia Hom e 1: richiesta vi sual i zzazi one brani Am m i ni stratore Com posizionePlayl istDB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 37 - Sequence Diagram Eliminazione brano MP3 dall'archivio 210 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.8 Ascolto di un singolo Brano Tra i servizi offerti dalla Web application, c’è la possibilità da parte dell’utente di poter ascoltare una sequenza di brani che compongono una propria playlist. La funzionalità di ascolto è messa a disposizione anche nel caso di un singolo MP3, sia per l’utente che per l’amministratore. Per poter usufruire di questa funzione, è ovviamente necessario accedere alle informazioni del singolo brano che si desidera ascoltare. Una volta effettuata tale richiesta, il sistema eseguirà il caricamento di un player MP3, che permetterà lo streaming del file associato al brano. L’avvio dell’ascolto, non impedisce all’utente di poter proseguire la navigazione all’interno dell’applicazione, usufruendo degli altri servizi. 211 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente/Amministratore Richiesta visualizzazione brani MP3 Sistema Visualizzazione elenco brani MP3 [Richiesta Selez ione modalità v is ualiz z az ione brani MP3 [Richiesta visualizzazione per ricerca] genere] Ricerca brani MP3 Caricamento elenco brani MP3 :BranoMP3 Ric hiesta ric erc a Bran [letto] Ric hiesta v is ualizz az ione brani MP3 per gener Visualizzazione brani MP3 Ins erimento c riteri di ric erc a [Assenza brani] [Presenza brani] Selezione brano MP3 Visualizzazione dati brano MP3 Caric amento dati brano MP3 :BranoMP3 [letto] Richiesta ascolto brano MP3 Vis ualiz z az ione dati MP3 Riproduzione brano MP3 :FileMP3 [streaming] Figura 38 - Activity Diagram Ascolto singolo brano MP3 212 Utente 2: create 2.2.17: riproduzione brano 2.2.11: richiesta ascolto brano 2.2.4: create Interfaccia Elenco Brani MP3 2.2.10: visualizzazione informazioni brano 2.2.3: richiesta visualizzazione info brano 2.2.2: selezione brano MP3 2.2.1: visualizzazione elenco brani 2.1.1: err: assenza brani Sequence Diagram Visualizzazione Brani MP3 per Genere o Ricerca 1: richiesta visualizzazione brani Interfaccia Home 2.2.19: fine riproduzione 2.2.16: riproduzione 2.2.12: ascolta( ) 2.2.9: informazioni brano 2.2.6: visualizzaInfo( ) 2.2.5: create Interfaccia Info Brano MP3 2.2.15: riproduzione 2.2.14: riproduci( ) 2.2.13: create :FileMP3 2.2.8: carica dati 2.2.7: leggi( ) 2.2.18: fine riproduzione :BranoMP3 BranoMP3DB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 39 - Sequence Diagram Ascolto singolo brano MP3 213 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.9 Gestione Playlist La gestione delle playlist rappresenta il servizio principale offerto dalla Web application per gli utenti registrati, anche esclusivamente con la modalità base. Attraverso questo servizio è possibile : - creare ed eliminare le proprie playlist preferite; - inserire e cancellare brani da ciascuna playlist; - ascoltare una playlist; L’operazione di creazione è relativamente semplice, in quanto l’unica informazione associata ad una playlist è il nome che può essere specificato dall’utente. Utente Inserimento informazioni Playlist Sistema Controllo correttezza dati [Dati non corretti] [Dati corretti] Registrazione informazioni Playlist :Playlist [creato] Figura 40 - Activity Diagram Creazione playlist 214 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Interfaccia Home PlaylistDB Utente 1: richiesta creazione playlist 2: create Interfaccia Creazione Playlist 3: richiesta inserimento informazioni playlist 4: inserimento informazioni playlist 5: esegui creazione playlist 6: controllo correttezza dati 6.1: err: dati non validi 6.2.1: create :Playlist 6.2.2: crea( ) 6.2.3: inserisci( ) 6.2.4: inserimento eseguito 6.2.5: inserimento eseguito 6.2.6: destroy 6.2.7: notifica creazione playlist Figura 41 - Sequence Diagram Creazione playlist Una volta create una o più playlist, è possibile visualizzarne l’elenco ed accedere a ciascuna di esse per visualizzarne i brani MP3 che la compongono. Gli Activity Diagram seguenti fanno riferimento a quanto detto e sono molto semplici in virtù del fatto che sono utilizzati come parti componenti dei diagrammi più complessi che prevedono queste funzionalità. Caricamenti elenco Playlist dall'archivio :Playlist [letto] Visualizzazione Playlist Figura 42 - Activity Diagram Visualizzazione elenco playlist 215 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Caricamento elenco brani MP3 in Playlist :BranoMP3 [letto] Visualizzazione contenuto Playlist Figura 43 - Activity Diagram Visualizzazione elenco brani MP3 nella playlist Per quanto riguarda i corrispondenti Sequence Diagram, non è previsto quello relativo alla visualizzazione delle playlist, in quanto le azioni necessarie vengono svolte direttamente nei diagrammi delle funzionalità che lo prevedono. Viceversa, è definito un diagramma la visualizzazione dei brani all’interno di una playlist, in quanto comporta uno scambio di messaggi molto più complessi che vale la pena mettere in risalto. La funzionalità di eliminazione delle playlist prevede due distinte modalità : - eliminazione di una o più playlist selezionandole dall’elenco; - eliminazione di una singola playlist, visualizzandone l’elenco dei brani che la compongono; In entrambi i casi, per ciascuna playlist ne vanno eliminate le informazioni caratteristiche e quelle relative alla composizione, ovviamente non cancellando dall’archivio i brani MP3. 216 2: create 6.2.3: selezione playlist 3: create 6.2.4: create :Playlist 6.2.9.2.2: visualizzazione brani nella playlist 6.2.9.1.2: notifica playlist vuota 6.2.1: playlist caricate 6.1.1: err: assenza playlist 4: visualizzaElencoPlaylist( ) Interfaccia Elenco Playlist 6.2.2: visualizzazione elenco playlist 6.1.2: notifica assenza playlist Utente 1: richiesta elenco playlist Interfaccia Home 6.2.10: destroy 6.2.9.2.1: brani caricati 6.2.9.1.1: err: playlist vuota 6.2.9: carica dati 6.2.8: leggi( ) 6.2.7: carica dati 6.2.6: leggi( ) 6.2.5: visualizzaElencoBraniMP3Playlist( ) Interfaccia Elenco Brani MP3 6: carica dati 5: leggi( ) PlaylistDB ComposizionePlaylistDB BranoMP3DB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 44 - Sequence Diagram Visualizzazione elenco brani MP3 nella playlist 217 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Sistema Visualizzazione elenco Playlist Richiesta visualizzazione elenco Playlist Caric amenti elenc o Play list dall'arc hivio :Play lis t [letto] Vis ualiz zazione Play lis t [Assenza Playlist] [Presenza Playlist] Selezione Playlist Visualizzazione elenco brani MP3 in Playlist Caricamento elenco brani MP3 in Playlist Selezione delle Playlist da cancellare :BranoMP3 [letto] Visualizzazione contenuto Playlist Richiesta cancellazione Playlist Cancellazione Playlist :Playlist [eliminato] Figura 45 - Activity Diagram Eliminazione playlist 218 Interfaccia Home 2: create 6.2.2.2.13: visualizzazione elenco playlist 6.2.2.2.12: destroy 6.2.3: destroy 6.2.2.2.11: notifica eliminazione eseguita 6.2.2.2.4: richiesta di cancellazione della playlist :Playlist 6.2.2.2.10: eliminazione eseguita 6.2.2.2.5: elimina( ) Interfaccia Elenco Brani MP3 6.2.2.1.8: eliminazione eseguita 6.2.2.1.3: elimina( ) 6.2.1: playlist caricate 6.1.1: err: assenza playlist 4: visualizzaElencoPlaylist( ) 3: create Sequence Diagram Visualizzazione Brani Playlist 6.2.2.2.3: visualizzazione elenco brani contenuti nella playlist Scenario 2 : eliminazione di Playlist selezionandone una e visualizzandone il contenuto 6.2.2.2.1: selezione playlist e richiesta visualizzazione brani contenuti 6.2.2.2.2: create Interfaccia Elenco Playlist 6.2.2.1.9: notifica eliminazione eseguita e visualizzazione elenco playlist Scenario 1 : eliminazione di Playlist selezionandole dall'elenco 6.2.2.1.2: esegui cancellazione 6.2.2.1.1: selezione playlist da cancellare 6.2.2: visualizzazione elenco playlist 6.1.2: notifica assenza playlist Utente 1: richiesta elenco playlist 6.2.2.2.9: eliminazione eseguita 6.2.2.2.8: elimina( ) 6.2.2.2.7: eliminazione eseguita 6.2.2.2.6: elimina( ) 6.2.2.1.7: eliminazione eseguita 6.2.2.1.6: elimina( ) 6.2.2.1.5: eliminazione eseguita 6.2.2.1.4: elimina( ) 6: carica dati 5: leggi( ) PlaylistDB ComposizionePlaylistDB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 46 - Sequence Diagram Eliminazione playlist Ovviamente l’utente ha la possibilità di inserire uno o più brani all’interno di una playlist, secondo le due seguenti modalità, evidenziate nel Sequence Diagram : - selezionando uno o più brani dall’elenco corrente; - inserendo un singolo brano, dall’interfaccia di visualizzazione delle informazioni; 219 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Sistema Richiesta visualizzazione brani MP3 Selez ione modalità v isualiz zaz ione brani MP3 Visualizzazione elenco brani MP3 [Richiesta ricerca] [Richiesta visualizzazione per genere] Ricerca brani MP3 Caricamento elenco brani MP3 :BranoMP3 Richies ta ric erca Bran [letto] Ric hies ta vis ualizz az ione brani MP3 per gene Visualizzazione brani MP3 Ins erimento c riteri di ric erc a [Assenza brani] [Presenza brani] Visualizzazione dati brano MP3 Selezione brano MP3 Caricamento dati brano MP3 :BranoMP3 [letto] Selezione dei brani MP3 da inserire Visualiz zaz ione dati MP3 Richiesta inserimento brano/i MP3 Inserimento brano/i MP3 nella Playlist :Playlist [aggiornato] Figura 47 - Activity Diagram Inserimento brano MP3 nella playlist Ovviamente, si esegue l’aggiornamento della composizione della playlist. 220 1: richiesta visualizzazione brani 2: create Interfaccia Elenco Brani MP3 2.2.2.2.3: create 2.2.2.2.17: visualizzazione elenco brani 2.2.3: destroy :BranoMP3 2.2.2.2.15: inserimento eseguito 2.2.2.2.12: inserisciBrano( ) 2.2.2.2.9: destroy 2.2.2.2.8: informazioni brano 2.2.2.2.5: visualizzaInfo( ) 2.2.2.2.4: create Interfaccia Info Brano MP3 2.2.2.1.6: inserimento eseguito 2.2.2.1.3: inserisciBrano( ) 2.2.1: create 2.2.2.2.16: notifica brano inserito nella playlist selezionata 2.2.2.2.11: selezione playlist e richiesta inserimento brano 2.2.2.2.10: visualizzazione informazioni brano Scenario 2 : inserimento di un brano in una playlist, selezionandolo e visualizzandone le informazioni 2.2.2.2.2: richiesta visualizzazione informazioni brano 2.2.2.2.1: selezione brano da inserire nella playlist 2.2.2.1.7: notifica brano/i inserito/i nella playlist selezionata Scenario 1 : inserimento di brani in una playlist, selezionandoli dall'elenco 2.2.2.1.2: esegui inserimento brano/i nella playlist 2.2.2.1.1: selezione playlist e brani da introdurre 2.2.2: visualizzazione elenco brani 2.1: err: assenza brani Sequence Diagram Visualizzazione Brani MP3 per Genere o Ricerca personalizzata Utente Interfaccia Home 2.2.2.1.4: modifica( ) ComposizionePlaylistDB 2.2.2.2.14: aggiornamento eseguito 2.2.2.2.7: carica dati 2.2.2.2.13: modifica( ) 2.2.2.1.5: aggiornamento eseguito 2.2.2.2.6: leggi( ) :Playlist BranoMP3DB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 48 - Sequence Diagram Inserimento brano MP3 nella playlist 221 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ L’eliminazione dei brani da una playlist prevede unicamente la possibilità di selezionarli dall’elenco. Utente Sistema Visualizzazione elenco Playlist Richiesta visualizzazione elenco Playlist Caricamenti elenco Play lis t dall'arc hiv io :Play list [letto] [Assenza Playlist] Vis ualiz zaz ione Playlis t [Presenza Playlist] Selezione Playlist Visualizzazione elenco brani MP3 in Playlist Richiesta visualizzazione brani MP3 in Playlist Caricamento elenco brani MP3 in Playlist :BranoMP3 [letto] [Assenza Brani] Visualizzazione contenuto Playlist [Presenza Brani] Selezione dei brani MP3 da cancellare Richiesta cancellazione brano/i MP3 Cancellazione brano/i MP3 da Playlist :Playlist [aggiornato] Figura 49 - Activity Diagram Eliminazione brani MP3 dalla playlist 222 Utente 2: create 4.1.7: notifica eliminazione brano/i selezionato/i 4.1.2: richiesta cancellazione brano/i selezionato/i 4.1.1: selezione brano/i da cancellare 4: visualizzazione elenco brani contenuti nella playlist Sequence Diagram Visualizzazione Brani Playlist 1: richiesta visualizzazione brani in una playlist Interfaccia Home 4.1.6: brano/i eliminato/i 4.1.3: eliminaBrano( ) 3: create Interfaccia Elenco Brani MP3 :Playlist 4.1.5: eliminazione eseguita 4.1.4: elimina( ) ComposizionePlaylistDB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 50 - Sequence Diagram Eliminazione brani MP3 dalla playlist 223 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ L’ultima funzionalità relativa alle playlist è relativa all’ascolto, ed è fornita dal sistema mediante l’ausilio di un player MP3 che viene avviato nel momento in cui ne viene fatta richiesta dall’utente. Utente Sistema Visualizzazione elenco Playlist Richiesta visualizzazione elenco Playlist Caric amenti elenco Play list dall'arc hiv io :Playlist [letto] Vis ualiz zazione Playlist [Assenza Playlist] [Presenza Playlist] Visualizzazione elenco brani MP3 in Playlist Selezione Playlist Caricamento elenco brani MP3 in Playlist :BranoMP3 [letto] Visualizzazione contenuto Playlist Richiesta ascolto Playlist Riproduzione Playlist :FileMP3 [streaming] Figura 51 - Activity Diagram Ascolto playlist 224 Utente 2: create 14: riproduzione playlist 5: richiesta ascolto playlist 4: visualizzazione brani contenuti nella playlist Sequence Diagram Visualizzazione Brani Playlist 1: richiesta visualizzazione brani playlist Interfaccia Home 17: fine riproduzione 13: riproduzione Per ogni brano della playlist 6: ascolta( ) 3: create Interfaccia Elenco Brani MP3 :Playlist 11: riproduzione 10: riproduci( ) 9: create :FileMP3 15: fine riproduzione :BranoMP3 16: fine riproduzione 12: riproduzione 8: ascolta( ) 7: create Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 52 - Sequence Diagram Ascolto playlist 225 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.10 Acquisto/Download Brano Se l’utente ha effettuato una registrazione “estesa” oppure, partendo da una registrazione “base”, ha deciso di acquistare una scheda prepagata in un secondo momento, automaticamente acquisisce il diritto di poter acquistare e scaricare i brani MP3 oltre che di ascoltarli in streaming. L’utente può effettuare l’acquisto di un brano partendo dalla visualizzazione di un elenco di brani ottenuti sulla base di una ricerca oppure contenuti all’interno delle proprie playlist precedentemente create. Selezionando il brano di interesse, accede alla scheda contenente le informazioni dello stesso ed ha poi la possibilità di confermarne l’acquisto. Ovviamente, il sistema esegue i dovuti controlli, verificando che l’importo residuo presente sulla scheda sia sufficiente all’acquisto. Nel caso di esito negativo, si invita l’utente alla ricarica della scheda mentre nel caso di esito positivo lo si abilita al download del brano. Contestualmente all’avvio del download, viene aggiornato l’importo residuo della scheda e viene registrato l’acquisto eseguito dall’utente, in modo da avere a disposizione un archivio storico con gli acquisti eseguiti e poter, ad esempio, generare delle statistiche dei brani maggiormente scaricati. 226 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Sistema Richiesta visualizzazione brani MP3 Visualizzazione elenco brani MP3 Richiesta visualizzazione elenco Playlist Visualizzazione elenco Playlist [Assenza Brani] [Presenza Brani] [Assenza Playlist] [Presenza Playlist] Selezione Playlist Richiesta visualizzazione brani MP3 in Playlist Visualizzazione elenco brani MP3 in Playlist [Assenza Brani] [Presenza Brani] Selezione brano MP3 Visualizzazione dati brano MP3 Richiesta Download brano MP3 Controllo Importo residuo su Scheda :SchedaPrepagata [letto] [Importo insufficiente] :FileMP3 [download] [Importo sufficiente] Registrazione Acquisto/Download brano MP3 :Download [registrato] :SchedaPrepagata [aggiornato] Avvio Download Registrazione acquisto brano MP3 ed aggiornamento Importo residuo della Scheda Figura 53 - Activity Diagram Acquisto/Download brano MP3 227 Utente 2: create 2.2.2: selezione brano MP3 2.2.1: visualizzazione elenco brani 2.2.16.2.22: notifica termine del download 2.2.16.2.14: esegui download 2.2.16.2.13: notifica acquisto eseguito ed abilitazione al download 2.2.16.1.3: notifica importo insufficiente per acquisto/download brano 2.2.11: richiesta acquisto/download brano 2.2.4: create Interfaccia Elenco Brani MP3 2.2.10: visualizzazione informazioni brano 2.2.3: richiesta visualizzazione info brano Sequence Diagram Visualizzazione Brani MP3 per Genere,Ricerca o in una Playlist 2.1: err: assenza brani Interfaccia Home 1: richiesta visualizzazione brani 2.2.5: create 2.2.6: visualizzaInfo( ) 2.2.16.2.21: destroy 2.2.16.2.20: termine download 2.2.16.2.15: avviaDownload( ) 2.2.16.2.12: acquisto effettuato :SchedaPrepagata 2.2.16.1.1: err: importo insufficiente 2.2.14: controlloImportoResiduo( ) 2.2.13: create 2.2.16.2.11: destroy 2.2.16.2.10: inserimento eseguito 2.2.16.2.7: inserisci( ) 2.2.16.2.6: create 2.2.16.2.5: destroy 2.2.16.2.4: aggiornamento eseguito 2.2.16.2.1: aggiornaImportoResiduo( ) :BranoMP3 2.2.16.1.2: err: importo residuo insufficiente 2.2.12: acquista( ) 2.2.9: informazioni brano Interfaccia Info Brano MP3 2.2.7: leggi( ) 2.2.15: leggi( ) 2.2.16.2.8: inserisci( ) 2.2.16.2.3: aggiornamento eseguito 2.2.16.2.2: modifica( ) 2.2.16: carica dati 2.2.16.2.19: destroy 2.2.16.2.18: termine download 2.2.16.2.17: download( ) 2.2.16.2.16: create 2.2.16.2.9: inserimento eseguito :Download 2.2.8: carica dati DownloadDB BranoMP3DB SchedaPrepagataDB :FileMP3 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 54 - Sequence Diagram Acquisto/Download brano MP3 228 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.11 Modifica Dati Personali L’utente e l’amministratore hanno entrambi l’opportunità di modificare i propri dati di accesso. In particolare, l’amministratore ha esclusivamente la possibilità di modificare la password, in quanto non ci sono ulteriori informazioni ad esso associate. Viceversa, per l’utente si possono presentare due scenari differenti: - nel caso in cui non possegga una scheda prepagata, avrà la possibilità di modificare la propria password e l’indirizzo email, ma non lo username; - nel caso in cui possegga una scheda prepagata, avrà ovviamente la possibilità di modificare i dati di accesso suddetti oltre a tutte le informazioni anagrafiche, fornite nella fase di acquisto della scheda; In entrambi i casi, il sistema visualizza i dati attuali, permettendo all’utilizzatore di modificarne i valori e confermarne l’aggiornamento. A questo punto, l’applicazione esegue in primo luogo un controllo di correttezza sui dati, per verificare che siano del formato atteso ed in particolare chiede all’utilizzatore, utente o amministratore che sia, di inserire una conferma della password scelta. Ovviamente, viene verificato che la password scelta e la sua conferma coincidano, altrimenti non si consente la modifica dei propri dati. La funzionalità di modifica dei dati, può essere considerata del tutto simile, senza alcuna distinzione, tra l’utente e l’amministratore, tenendo esclusivamente conto dei limiti di aggiornamento che può eseguire il secondo. Si è preferito comunque sviluppare gli Activity e Sequence Diagram in maniera separata, considerando che gli oggetti in gioco sono sostanzialmente diversi, poiché fanno riferimento a due utilizzatori differenti. 229 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Utente Richiesta modifica dati personali Sistema Caricamento dati attuali :Utente [letto] Inserimento nuovi dati Visualizzazione dati attuali Controllo correttezza dati [Dati non corretti] [Dati corretti] Controllo Password vecchia [Password errata] [Password corretta] Controllo Password nuova e conferma [Password nuova e conferma non coincidono] [Password nuova e conferma coincidono] Registrazione nuovi dati :Utente [aggiornato] Visualizzazione Menu Utente Figura 55 - Activity Diagram Modifica dati personali Utente 230 2: create 11.2.2.2.6: aggiornamento dati eseguito 11.2.2.1.2: notifica Password nuova e Conferma non coincidono 11.2.1.2: notifica Password corrente errata 11.1: err: dati non validi 10: esegui modifica dati 9: inserimento nuovi dati 3: create 12: destroy 11.2.2.2.5: aggiornamento eseguito 11.2.2.2.2: modificaDatiAnagrafici( ) 11.2.2.2.1: Password e Conferma coincidono 11.2.2.1.1: err: Password nuova e Conferma diverse 11.2.1.1: err: Password corrente errata 11.2: modificaUserNamePassword( ) 11: controllo correttezza dati 7: dati attuali Utente 4: visualizzaDatiAnagrafici( ) Interfaccia Modifica Dati Personali 8: visualizzazione dati attuali dell'Utente Utente 1: richiesta modifica dati personali Interfaccia Home :Utente 5: leggi( ) 11.2.2.2.4: aggiornamento eseguito 11.2.2.2.3: modifica( ) 6: carica dati UtenteDB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 56 - Sequence Diagram Modifica dati personali Utente 231 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore Sistema Inserimento Password vecchia, nuova e conferma Controllo Password vecchia :Amministratore [letto] [Password errata] [Password corretta] Controllo Password nuova e conferma [Password nuova e conferma non coincidono] [Password nuova e conferma coincidono] Registrazione nuova Password :Amministratore [aggiornato] Visualizzazione Menu Amministratore Figura 57 - Activity Diagram Modifica password Amministratore 232 2: create 8.2.2.4: aggiornamento Password eseguito 8.2.1.2: notifica Password nuova e Conferma non coincidono 8.1.2: notifica Password corrente errata 5: esegui cambio Password 4: inserimento dati 9: destroy 8.2.2.3: aggiornamento eseguito 8.2.2.1: modifica( ) 8: carica dati 7: leggi( ) AmministratoreDB 8.2.2.2: aggiornamento eseguito :Amministratore 8.2.1.1: err: Password nuova e Conferma diverse 8.1.1: err: Password corrente errata 6: modificaUserNamePassword( ) Interfaccia Modifica Password 3: richiesta inserimento dati per cambio Password Amministratore 1: richiesta modifica Password Interfaccia Home Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 58 - Sequence Diagram Modifica password Amministratore 233 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.3.12 Gestione Utenti L’amministratore ha accesso ad una particolare sezione della Web application, mediante la quale può effettuare delle semplici operazioni relative alla gestione degli utenti. La prima funzionalità disponibile è quella che permette la visualizzazione dell’elenco degli utenti registrati, con la possibilità di selezionare uno o più utenti e di eseguirne la cancellazione dal sistema. A partire da tale elenco, è inoltre possibile selezionare uno degli utenti per poterne visualizzare le informazioni di registrazione, ovviamente esclusa la password di accesso al sistema. Le informazioni visualizzate dipendo dal tipo di registrazione che ha eseguito l’utente e dal fatto che possegga o meno una scheda prepagata. In virtù di questa distinzione, saranno visualizzate solo le informazioni di accesso oppure anche i dati anagrafici. In corrispondenza dell’interfaccia di visualizzazione dei dati del singolo utente, l’amministratore può eseguire due semplici operazioni : - eliminare l’utente; - sbloccare l’utente, qualora quest’ultimo risulti bloccato in virtù del fatto che sono stati eseguiti due accessi consecutivi errati al sistema; Il Sequence Diagram evidenzia i due scenari possibili per la cancellazione di uno o più utenti, nonché la funzionalità di sblocco. 234 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Amministratore Sistema Richiesta visualizzazione elenco Utenti Caricamento elenco Utenti :Utente [letto] : 1 [Assenza Utenti] Visualizzazione elenco Utenti [Presenza Utenti] Selezione Utente :Utente [cancellato] Richiesta cancellazione Utente Richiesta visualizzazione dati Utente Cancellazione Utente dall'archivio Caricamento dati Utente :Utente [letto] : 2 [Cancella Utente] Richiesta sblocco Utente Visualizzazione dati Utente [Uscita dal servizio] Sblocco Utente :Utente [sbloccato] Visualizzazione Menu Amministratore Figura 59 - Activity Diagram Gestione Utenti 235 Interfacci a Home 2: create 7.2.7.2.5: noti fi ca sbl occo esegui to 7.2.7.2: ri chi esta sbl occo utente 7.2.7.1.6: vi sual i zzazi one el enco utenti regi strati 7.2.7.1: ri chi esta el i m i nazi one utente 7.2.7: vi sual i zzazi one dati anagrafi ci utente Scenari o 2 : cancel l azi one di un si ngol o Utente oppure sbl occo di un Utente 7.2.1: ri chi esta vi sual i zzazi one dati anagrafi ci di un Utente sel ezi onato 7.3: destroy 7.2.7.2.4: sbl occo esegui to 7.2.7.2.1: sbl occa( ) 7.2.7.1.4: el i mi nazi one esegui ta 7.2.7.1.1: el i m i na( ) 7.2.6: dati anagrafi ci utente 7.2.3: vi sual i zzaDati Anagrafi ci ( ) Interfacci a Anagrafi ca Utente 7.1.5: el i mi nazi one esegui ta 7.1.2: el i mi na( ) 6.2.1: el enco utenti regi strati 6.1: err: non ci sono utenti regi strati 4: vi sual i zzaEl encoUtenti ( ) 3: create 7.2.7.1.5: noti fi ca el i mi nazi one esegui ta 7.2.2: create Interfacci a El enco Utenti 7.1.6: noti fi ca el i mi nazi one esegui ta e vi sual i zzazi one el enco utenti regi strati Scenari o 1 : cancel l azi one utenti sel ezi onandol i dal l 'el enco 7.1.1: sel ezi one utente/i da cancel l are 6.2.2: vi sual i zzazi one el enco utenti regi strati 6.1.1: noti fi ca errore non ci sono utenti regi strati Ammi ni stratore 1: ri chi esta vi sual i zzazi one el enco utenti :Utente 5: l eggi ( ) 7.2.7.2.3: aggi ornamento esegui to 7.2.7.2.2: modi fi ca( ) 7.2.7.1.3: el i m i nazi one esegui ta 7.2.7.1.2: el i m i na( ) 7.2.5: cari ca dati 7.2.4: l eggi ( ) 7.1.4: el i mi nazi one esegui ta 7.1.3: el i mi na( ) 6: cari ca dati UtenteDB Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Figura 60 - Sequence Diagram Gestione Utenti 236 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.4 Statechart Diagrams Gli Statechart Diagrams costituiscono uno strumento mediante il quale è possibile descrive una state machine, enfatizzando il flusso di controllo tra gli state. Una state machine definisce un comportamento che specifica i vari state che un oggetto può assumere durante la sua vita in risposta a certi eventi. Ogni state rappresenta una condizione di un oggetto, in cui sono soddisfatti certi requisiti , mentre un evento è la specificazione di un accadimento significativo, posizionabile nel tempo e nello spazio, che determina una transizione di stato da parte dell’oggetto. Quest’ultima esprime il fatto che, un oggetto a partire da un certo stato ed al verificarsi di un determinato evento passa in uno stato differente, poiché sono cambiate le condizioni in cui si trova. Sulla base di quanto detto, un diagramma di questo tipo può rappresentare un’estensione della descrizione di un automa a stati finiti. Considerando il case study sviluppato, sono stati definiti gli Statechart Diagrams per quegli oggetti che possono compiere delle transizioni significative durante la vita della Web application. In particolare sono : - Amministratore; - Utente; - BranoMP3; - CartaCredito; - Playlist; - SchedaPrepagata Per quanto riguarda l’Amministratore, quest’ultimo può trovarsi fondamentalmente in due possibili stati : loggato e bloccato. Ovviamente, bisogna tener conto che la condizione di Amministratore non loggato rappresenta il punto di inizio del diagramma. Lo stato “loggato” si raggiunge dopo aver eseguito correttamente il login, mentre lo stato “bloccato” dopo aver commesso due errori consecutivi di accesso. 237 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ / 2 errori consecutivi di login Bloccato / login Loggato / sblocco / logout Figura 61 - Statechart Amministratore L’Utente prevede una situazione leggermente complicata, in quanto si parte da uno stato iniziale in cui l’utente non è registrato. Una volta eseguita la registrazione, l’utente passa nello stato “registrato” ed all’interno di esso può evolvere tra gli stessi stati dell’amministratore, ossia “loggiato” e “bloccato”, sulla base dei medesimi eventi. Lo Statechart Diagram prevede una struttura innestata, nell’ambito della quale all’interno dello stato “registrato” è previsto un ulteriore diagramma che descrive le possibili transizioni di stato dell’utente che ha eseguito la registrazione. 238 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ / registrazione Utente Registrato / 2 errori consecutivi di login / login Loggato Bloccato / logout / sblocco / cancellazione Utente Figura 62 - Statechart Utente Il BranoMP3 ha un’evoluzione notevolmente complicata, considerando che si parte da una condizione in cui il brano non è stato inserito nell’archivio ed eseguendo tale operazione, si passa allo stato “archiviato”. In corrispondenza di tale stato, il brano può subire diverse transizioni : - passare nello stato di “in ascolto”; - passare nello stato di “acquistato”; - passare nello stato “in playlist”; 239 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Lo stato “acquistato” a sua volta prevede la possibilità, da parte del brano, di passare nello stato di “downloading”, considerando che la condizione necessaria per scaricare un brano è effettuarne l’acquisto. Allo stesso modo, lo stato “in playlist” permette al brano di passare nello stato “in ascolto” ed “acquistato”, per poter esplicitare il fatto che l’ascolto e l’acquisto di un brano MP3 possono essere eseguiti nei casi in cui l’utente abbia o meno delle playlist preferite. / inserimento brano Archiviato / ascolta / aggiungi a playlist / acquista Acquistato In Playlist / ac quis ta / as c olta / avvio download In Ascolto Ac quis tato2 Downloading / termine download / acquis to es eguito in As c olto / fine asc olto / fine ascolto / acquisto eseguito / cancella da playlist / cancellazione brano Figura 63 - Statechart BranoMP3 240 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ / acquista / avvio download / ascolta Downloading Acquistato2 / termine download / acquisto eseguito in Ascolto / fine ascolto Figura 64 - Statecharts BranoMP3 acquistato/in playlist L’oggetto CartaCredito è un’evoluzione abbastanza semplice, in quanto l’unico stato in cui può transitare è “in verifica”, nell’ambito del quale il sistema sta valutando se la carta di credito sia coperta o meno. / verifica copertura In Verifica / termine verifica Figura 65 - Statechart CartaCredito 241 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ La Playlist può essere paragonato al BranoMP3 ma con una complessità inferiore. Eseguendo l’operazione di creazione, la Playlist passa nello stato “archiviata”, nell’ambito del quale può trovarsi o meno nello stato di “ascolto”. / creazione Archiviata / ascolta Ascolto / f ine ascolto / cancellazione Figura 66 - Statechart Playlist L’oggetto SchedaPrepagata prevede probabilmente una delle evoluzioni più complesse. Effettuando l’operazione di acquisto, da parte dell’utente, la scheda passa nello stato “acquistata”, all’interno del quale può effettuare diverse transizioni. 242 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ / acquisto Acquistata / scaduta Figura 67 - Statechart SchedaPrepagata Lo stato “acquistata” è composto, per cui all’interno di esso può essere definito un ulteriore Statechart Diagram certamente più complesso. / scadenza scheda / richiesta ricarica Scaduta / acquisto brano / richiesta ricarica In Uso / importo residuo aggiornato / importo residuo esaurito Ricaricata / richiesta ricarica Esaurita / termine ricarica Figura 68 - Statechart SchedaPrepagata acquistata 243 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ All’interno del diagramma si possono evidenziare i seguenti stati : - “scaduta” : quando la data odierna va oltre la data di scadenza della scheda; - “in uso” : quando ne viene aggiornato l’importo residuo, perché è stato effettuato l’acquisto di un brano MP3; - “esaurita” : quanto l’importo residuo è azzerato; - “ricaricata” : quando viene eseguita un’operazione di ricarica; 3.5 Conceptual Data Model La definizione del modello concettuale rappresenta il primo passo verso la progettazione della base di dati. Esso permette di descrivere tutte quelle che sono le entità del mondo reale e le relazioni che ci sono fra di esse. Inoltre, per ciascuna entità o relazione che sia, è possibile definirne gli attributi caratteristici. In un certo senso, lo stesso Class Diagram può essere considerato in moltissimi casi il modello concettuale di un database, poiché le entità e le relazioni rappresentate sono praticamente le stesse, a meno di un formalismo differente. Facendo riferimento alla Web application in esame, le entità del modello sono le seguenti : - AccessoUtente ed AccessoAmministratore : rappresentano le informazioni di accesso dell’utente e dell’amministratore. Esse rappresentano una specializzazione dell’entità Accesso, alla quale sono legate con una relazione del tipo Generalizzazione/Specializzazione. - Utente ed Amministratore : definiscono le informazioni di un utente e dell’amministratore. Sono una specializzazione dell’entità Ruolo; - BranoMP3 : contiene le informazioni di un brano MP3; - Playlist : rappresenta la generica playlist creata da un utente; - SchedaPrepagata : rappresenta le informazioni della scheda prepagata; E’ da osservare che, rispetto al Class Diagram non è presente l’entità CartaCredito, in virtù del fatto che l’applicazione non memorizza in maniera permanente le sue 244 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ informazioni all’interno della base di dati, ma utilizza sempre un oggetto in memoria che al termine dei controlli necessari viene distrutto. Inoltre, le relazioni che legano queste entità sono : - LoginLogout Utente : associa a ciascun Utente le relative informazioni di accesso dell’entità AccessoUtente; - LoginLogout Amministratore : associa l’Amministratore alle informazioni relative agli accessi eseguiti dell’entità AccessoAmministratore; - Download : associa l’Utente con un BranoMP3. E’ da osservare che nel Class Diagram, questa entità è rappresentata da una classe associativa; - Creazione : lega l’Utente con la Playlist. Essa non è presente nel Class Diagram; - Composizione : descrive la composizione di una playlist, associando un BranoMP3 con la Playlist di appartenenza. E’ prevista anche nel Class Diagram ma con un formalismo diverso, la relazione di “aggregazione”; - Acquisto e Ricarica : legano l’Utente con la SchedaPrepagata. E’ da osservare che tra queste due entità sussistono due relazioni, poiché da un punto di vista concettuale sono differenti e ciascuna di esse ha delle informazioni proprie, diverse dall’altra; Infine, per ciascuna entità e per ciascuna relazione sono specificati gli attributi che le caratterizzano con il corrispondente tipo di dato. E’ da osservare che alcune relazioni non hanno attributi, in quanto servono ad esprimere esclusivamente un’associazione tra due entità ma non hanno informazioni proprie. Nel modello logico, alcune di queste sono destinate a scomparire, ossia LoginLogout Utente e LoginLogout Amministratore, mentre Composizione sarà necessaria per definire la relazione molti-amolti che esiste tra BranoMP3 e Playlist. 245 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Accesso dataOraLogin dataOraLogout loginValido tentativo indirizzoIP DT DT BL BT VA15 <<extends>> AccessoUtente Accesso <<extends>> AccessoAmministratore Accesso Ruolo AccessoUtente userName password bloccato loggedIn 1,1 AccessoAmministratore VA20 VA15 BL BL 1,1 LoginLogout Amministratore LoginLogout Utente <<extends>> Amministratore Ruolo <<extends>> Utente Ruolo 0,n Amministratore 0,n Utente nome cognome indirizzo citta provincia CAP stato telefono dataNascita sesso email dataOraRegistrazione acquistoMP3 VA15 VA15 VA50 VA30 VA30 VA5 VA15 VA15 D A1 VA50 DT BL Download 0,n BranoMP3 dataOraAcquisto DT 0,n 0,n genere titolo autore album durata costo dimensione nomeFileMP3 VA15 VA50 VA30 VA50 T DC3,2 I VA255 0,n Creazione dataOraCreazione DT 0,1 0,n Composizione Ricarica Acquisto importo DC5,2 dataOra DT dataOraAcquisto DT 0,n Playlist 1,1 1,1 nome VA15 SchedaPrepagata importoResiduo DC5,2 dataScadenza D 0,n Figura 69 - Conceptual Data Model 246 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ 3.6 Physical Data Model Per completare la progettazione della base di dati, è necessario effettuare la trasformazione del modello concettuale nel modello logico, anche detto modello fisico. Quest’ultimo è costituito da una serie di tabelle e le relative colonne che prendono il posto delle entità e delle relazioni con i relativi attributi, presenti nel modello concettuale. La trasformazione viene eseguita sulla base di una serie di regole, che permettono di adattare il mondo reale, rappresentato dal modello concettuale, all’implementazione fisica che verrà successivamente adottata per uno specifico DBMS (DataBase Management System). Le principali operazioni di trasformazione sono le seguenti : - eliminare tutte le relazioni del tipo Generalizzazione/Specializzazione, sulla base di tre possibili alternative; 1. accorpamento delle entità figlie nel padre, con l’introduzione di un attributo che le possa distinguere; 2. accorpamento del padre nelle entità figlie; 3. sostituzione delle generalizzazioni con semplici associazioni; - scegliere uno o più attributi di ciascuna entità che possano costituirne una chiave primaria, ossia tale da individuare ogni istanza dell’entità stessa in maniera univoca. Nel caso in cui l’entità non sia caratterizzata da attributi con questo tipo di funzionalità, è possibile introdurre un nuovo attributo che abbia il solo ruolo di chiave primaria; - ogni entità va rappresentata con una tabella, le cui colonne sono esattamente gli attributi dell’entità stessa; - una relazione uno-a-molti tra due entità non viene rappresentata con una tabella ma viene assorbita dall’entità lato “uno”; - una relazione molti-a-molti viene rappresentata attraverso una tabella che sarà legata alle due tabelle relative alle entità in gioco, sulla base delle loro chiavi primarie; - una relazione uno-a-uno non viene rappresentata attraverso una tabella ma è assorbita indifferentemente da una delle entità legate; 247 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Accesso idAccesso idRuolo ruolo dataOraLogin dataOraLogout loginValido tentativo indirizzoIP int <pk> int <fk1,fk2> char datetime datetime bool tinyint unsigned varchar(15) ruolo : distingue tra le entità figlie (AccessoUtente ed AccessoAmministratore) idRuolo : è l'ID di un Utente o di un Amministratore FK_IDAMMINISTRATORE FK_IDUTENTE Download idUtente int <fk1> idBranoMP3 int <fk2> dataOraAcquisto datetime Amministratore idAmministratore userName password bloccato loggedIn int <pk> varchar(20) varchar(15) bool bool FK_IDUT ENT E FK_IDBRANOMP3 Utente idUtente nome cognome indirizzo citta provincia CAP stato telefono dataNascita sesso email dataOraRegistrazione acquistoMP3 userName password bloccato loggedIn BranoMP3 int <pk> varchar(15) varchar(15) varchar(50) varchar(30) varchar(30) varchar(5) varchar(15) varchar(15) date char varchar(50) datetime bool varchar(20) varchar(15) bool bool idBranoMP3 genere titolo autore album durata costo dimensione nomeFileMP3 int <pk> varchar(15) varchar(50) varchar(30) varchar(50) time decimal(3,2) int unsigned varchar(255) FK_IDBRANOMP3 FK_IDUTENTE ComposizionePlaylist idPlaylist int <fk1> idBranoMP3 int <fk2> FK_IDUT ENT E Ricarica idRicarica idSchedaPrepagata importo dataOra int <pk> int <fk> decimal(5,2) datetime FK_IDPLAYLIST Playlist SchedaPrepagata idSchedaPrepagata idUtente importoResiduo dataScadenza dataOraAcquisto FK_IDSCHEDAPREPAGATA int <pk> int <fk> decimal(5,2) date datetime idPlaylist idUtente nome dataOraCreazione int <pk> int <fk> varchar(15) datetime Figura 70 - Physical Data Model 248 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ Sulla base delle regole suddette, il modello logico risultante ha le seguenti caratteristiche : - in tutte le tabelle è stato introdotto un attributo (colonna) che ne rappresenti la chiave primaria, con un nome del tipo “idNomeTabella” per esprimere che si tratta di un identificativo univoco; - la relazione Generalizzazione/Specializzazione avente come padre l’entità Accesso e come figlie le entità AccessoUtente ed AccessoAmministratore è stata eliminata accorpando queste ultime nel padre. Per poter distinguere le occorrenza dell’una e dell’altra, è stata introdotto l’attributo “ruolo” nella tabella Accesso definitiva. La scelta della soluzione da adottare è basata sul fatto che le due entità figlie sono caratterizzate da occorrenza concettualmente diverse ma che hanno le medesime informazioni; - la relazione Generalizzazione/Specializzazione avente come padre l’entità Ruolo e come figlie le entità Utente ed Amministratore è stata eliminata accorpando all’interno di queste ultime, l’entità padre. Tutto ciò determina la presenza di due tabelle distinte per l’utente e per l’amministratore ed è da sottolineare che la scelta fatta è motivata dalla presenza di informazioni completamente diverse che caratterizzano i due utilizzatori; - le relazioni uno-a-molti da Utente ed Amministratore verso Accesso, sono state risolte introducendo all’interno di quest’ultima tabella una chiave esterna “idRuolo” che corrisponde alle chiavi primarie “idUtente” ed “idAmministratore”. E’ da osservare che l’identificazione univoca di un accesso, appartenente ad un utente oppure all’amministratore, è individuata dalla coppia “idRuolo-ruolo”; - la relazione molti-a-molti tra Utente e BranoMP3 è stata trasformata nella tabella Download contenente le chiavi primarie di queste ultime. Esse, considerate in coppia, formano la chiave primaria della tabella stessa; - la relazione uno-a-molti tra Utente e Playlist è stata assorbita dal lato uno (Playlist) trasferendone il proprio attributo. Per questo motivo, la tabella Playlist ha una chiave esterna che la lega alla tabella Utente; 249 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ - la relazione molti-a-molti tra BranoMP3 e Playlist relativa alla composizione di quest’ultima è stata trasformata nella tabella Composizione, che contiene semplicemente le chiavi primarie delle due tabelle legate; - la relazione uno-a-uno tra Utente e SchedaPrepagata poteva essere risolta attraverso una sola tabella, ma in questo caso si sarebbe persa la differenza concettuale che c’è tra le due entità. Per questo motivo sono state definite due tabelle separate e la relazione è stata assorbita dal lato SchedaPrepagata, contenendo attributi che riguardano strettamente quest’ultima; - la relazione molti-a-molti tra Utente e SchedaPrepagata relativa alle operazioni di ricarica è stata trasformata nella tabella Ricarica. Essa prevede una chiave esterna che la lega alla tabella SchedaPrepagata. Il legame con la tabella Utente è stato eliminato in quanto, non solo dal punto di vista concettuale la ricarica è strettamente legata alla scheda, ma anche perché si sarebbe venuta a creare un’inutile relazione circolare. E’ sempre possibile passare dall’Utente alla Ricarica attraverso la SchedaPrepagata; Oltre queste trasformazioni, sono stati introdotti dei particolari vincoli (costraints) di “cascade” sugli eventi “onDelete”, in modo da non lasciare informazioni inutili che rendano il database “sporco”. Qui di seguito sono elencate le tabelle interessate, le chiavi esterne e le motivazioni dei vincoli : - le due chiavi esterne della tabella Composizione, in modo che se viene cancellato un brano MP3 dall’archivio, esso scompare anche dalle composizioni delle playlist. Inoltre, se viene cancellata una playlist, vengono cancellati tutti i riferimenti ai brani che la componevano; - le due chiavi esterne della tabella Download, in modo che se si cancella un utente si perdono anche le tracce dei brani scaricati, così come se viene cancellato un brano non si hanno più riferimenti agli utenti che ne hanno eseguito il download; - la chiave esterna della tabella SchedaPrepagata verso Utente, in modo che cancellando un utente vengono automaticamente eliminate le informazioni di un’eventuale scheda prepagata ad esso associata; 250 Capitolo V – Case Study : Analisi e Progettazione _________________________________________________________________ - la chiave esterna della tabella Ricarica, in modo che cancellando una scheda prepagata vengono eliminate anche tutte le operazioni di ricarica eseguite su di essa; - la chiave esterna della tabella Playlist, in modo che cancellando un utente siano eliminate anche le playlist ad esso associate; Attraverso i legami che ci sono tra le tabelle ed i vincoli di “cascade” definiti sugli eventi “onDelete”, si determinano delle operazioni di cancellazione a catena che permettono di lasciare nel database solo ed esclusivamente i dati necessari. Ad esempio, cancellando un utente vengono eliminate le playlist ad esso associate e quindi le informazioni della loro composizione, nonché un eventuale scheda prepagata con tutte le operazioni di ricarica ed infine le operazioni di download dei brani MP3. Questo è il più completo degli scenari che si possa presentare nelle operazioni di cancellazione delle informazioni dalla base di dati. 251 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Capitolo VI – Case Study : Implementazioni a confronto 1. Introduzione Il confronto tra due tecnologie, che nel caso specifico sono i due framework di sviluppo Web Struts e JavaServer Faces, può essere considerato effettivamente valido soltanto nel momento in cui viene supportato da una prova sul campo. Infatti, non basta semplicemente descrivere in maniera puramente discorsiva le caratteristiche dell’uno e dell’altro framework, ma è necessario applicare le due tecnologie ad un medesimo caso di studio, per poter dare maggior risalto al confronto teorico. Per questo motivo, la Web application precedentemente progettata è stata implementata sia con Struts che JSF, in modo da valutare con quali modalità i due framework permettano di realizzare le medesime funzionalità. Ovviamente, facendo seguito alla stessa fase di Progettazione, il sistema software risultante ha un’architettura diversa per ciascuna tecnologia, sulla base delle potenzialità che differenziano l’una dall’altra. I termini di paragone possono riguardare : - la semplicità con cui un framework permetta di realizzare una funzionalità rispetto all’altro, attraverso gli strumenti che fanno parte della propria architettura; - la necessità da parte di un framework di utilizzare strumenti esterni alla propria implementazione; - la possibilità di estensione e la flessibilità di un framework rispetto all’altro, in modo da poter definire degli strumenti di sviluppo personalizzati; Sulla base di questi aspetti può essere impostato un confronto pratico, che non faccia altro che confermare quanto descritto nel Capitolo IV. 252 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per quanto riguarda il caso di studio preso in esame, gli strumenti software utilizzati per lo sviluppo e gli ambienti di esecuzione sono i seguenti : - Piattaforma J2SE (Java 2 Second Edition); - Ambiente IDE Eclipse 3.1 con i plug-in di Exadel Studio, per lo sviluppo grafico dei file di configurazione di JavaServer Faces e Struts, e UML Omondo, per la realizzazione dei Class Diagrams per le classi implementate; - Web Container/Server Jakarta Apache Tomcat 5.0.28; La piattaforma J2SE (Java2 Standard Edition) rappresenta la base per lo sviluppo e l’esecuzione delle applicazioni Java, in quanto mette a disposizione la JVM (Java Virtual Machine), oltre a tutte le classi principali e le librerie del linguaggio. Un’estensione di questa piattaforma è rappresentata dalla J2EE (Java2 Enterprise Edition) che fornisce il supporto per strumenti avanzati come i Web Services, gli EJB (Enterprise Java Beans) e JavaServer Faces. In questo caso, non si è reso necessario l’utilizzo della piattaforma J2EE, in quanto la distribuzione del framework JSF può essere gestita anche semplicemente mediante le librerie JAR (Java ARchive) che contengono le classi dell’architettura. Nelle successive versioni della piattaforma Java, in particolare a partire dalla Java5, JSF ne diventerà parte integrante. Per l’ambiente di sviluppo IDE (Integrated Development Environment), la scelta è caduta sul progetto OpenSource Eclipse 3.1, dopo un rapido confronto con l’antagonista NetBeans. Ha nettamente prevalso il primo, considerando il supporto fornito per lo sviluppo di applicazioni Web basate appunto su Struts e JSF, grazie al plug-in free di Exadel. Il secondo, invece, fino alla versione 4.1 non ha ancora fornito tale supporto, introdotto a partire dalla versione 5 in fase di Beta Testing. Prendendo in esame tale versione, la semplicità ed il supporto offerti da Eclipse 3.1 sono comunque nettamente superiori. L’ambiente è stato ulteriormente ampliato utilizzando il plug-in free di Omondo per la realizzazione dei diagrammi UML, a partire dalle classi implementate. Tale strumento è stato utilizzato solo ed esclusivamente per generare i Class Diagrams a livello di implementazione. 253 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per quanto riguarda il Web Container, la scelta è ovviamente caduta sul progetto OpenSource Jakarta Apache Tomcat 5.0.28, sicuramente tra i migliori per l’esecuzione di applicazioni Web realizzate in ambiente Java. E’ altresì necessario specificare che, per quanto riguarda Struts, è stata utilizzata la versione 1.1 mentre per JavaServer Faces è stata scelta l’implementazione JSF-RI (Refernce Implementation) 1.0 e non MyFaces. In questo modo, è possibile confrontare due tecnologie realizzate da sviluppatori diversi, in quanto Struts rientra tra i progetti Jakarta, così come MyFaces, mentre JSF-RI è stato realizzato dalla Sun Microsystems. 2. Struttura dell’applicazione Il primo aspetto preso in considerazione riguarda la struttura dell’applicazione, in termini di eventuali moduli che la compongono, oltre che delle pagine, le immagini, gli eventuali fogli di stile (CSS) e di tutti gli altri elementi che ve ne fanno parte. Per quanto riguarda l’implementazione con JSF, l’architettura del sistema prevede un unico modulo al quale è ovviamente associato un file di configurazione (facesconfig.xml). I file componenti il progetto riguardano l’applet che implementa il player MP3 per l’ascolto dei brani, i templates che definiscono il layout delle pagine mediante l’utilizzo di Tiles ed il relativo file delle definizioni (tiles-defs.xml), le pagine ed i relativi contenuti, le immagini, il foglio di stile (styles.css) per la formattazione della grafica ed un Tag Library Descriptor (components.tld) che contiene i tag associati ai componenti della UI implementati. Infine, oltre alla librerie che contengono le classi costituenti l’architettura del framework, sono utilizzati i seguenti gruppi di ulteriori librerie : - JavaMail : framework per l’invio delle mail basato su JAF (JavaBeans Activation Framework); - JDBC MySQL Connector : contiene le classi relative ai driver JDBC (Java DataBase Connectivity) per l’accesso alla base di dati realizzata con il DBMS MySQL; 254 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - Tiles : framework per la definizione del layout delle pagine; - Java MP3 Tag Library : classi per l’accesso ai tag ID3v1 dei file MP3; - Commons Project File Upload e Tomahawk : contiene una serie di funzionalità aggiuntive, tra cui il componente per eseguire l’upload di un file sul server; Per quanto riguarda l’implementazione con Struts, l’applicazione ha un’architettura che prevede tre moduli distinti : - default : modulo principale che fornisce le funzionalità generiche di login, registrazione di un nuovo utente e richiesta dei dati di accesso via mail; - user : modulo contenente tutte le funzionalità disponibili ad un utente registrato; - admin : modulo che fornisce le funzionalità per l’amministratore; Ciascuno di questi moduli ha il proprio file di configurazione all’interno del quale descrivere il mapping delle action, i forward, le pagine e le eccezioni. Il modulo “default” viene avviato al momento della prima richiesta di un client. Nel momento in cui l’utilizzatore effettua un’operazione di login, in base al fatto che si tratti di un utente oppure dell’amministratore, viene eseguito lo “switch” verso il modulo corrispondente. Invece, se viene eseguita un’operazione di registrazione, poiché questa è seguita da un login automatico, si passa direttamente al modulo “user”. Il passaggio inverso dai moduli “user” e “admin” verso il modulo “default” viene effettuato in seguito ad un’azione di logout. Attraverso la scomposizione in moduli, non soltanto si ha il notevole vantaggio di raggruppare concettualmente le funzionalità legate ad un particolare utilizzatore ma anche quello di dover gestire tre file di configurazione differenti di dimensione ridotta, in luogo di un unico file di grandi dimensioni. La struttura in termini di file prevede, similmente a JSF, l’applet che implementa il player MP3, i templates per il layout delle pagine mediante Tiles ed il relativo file delle definizioni (tiles-defs.xml), le pagine ed i relativi contenuti, le immagini, il foglio di stile (styles.css) per la formattazione della grafica. Tali file sono opportunamente separati in corrispondenza dei tre moduli. 255 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Oltre alle librerie proprie di Struts, che includono Tiles ed il plug-in Validator, sono utilizzati anche i seguenti gruppi di librerie : - JavaMail : framework per l’invio delle mail basato su JAF (JavaBeans Activation Framework); - JDBC MySQL Connector : contiene le classi relative ai driver JDBC (Java DataBase Connectivity) per l’accesso alla base di dati realizzata con il DBMS MySQL; - Java MP3 Tag Library : classi per l’accesso ai tag ID3v1 dei file MP3; - JSTL : contiene i tag, che sfruttano l’Expression Language, per la costruzione del contenuto delle pagine; Rispetto all’implementazione in JSF, si evince che il framework Tiles è incluso nella distribuzione di Struts ed è perfettamente integrato con quest’ultimo, così come non è necessario il Commons Project File Upload, in quanto la libreria Struts HTML fornisce un tag per l’upload di file. L’utilizzo di JSTL può essere considerato non necessario, in quanto le librerie del framework forniscono tutto il necessario, anche se JSTL supporta l’Expression Language che permette di semplificare l’accesso alle proprietà dei JavaBeans mediante l’utilizzo della “dot notation”. Il plug-in Exadel dell’ambiente Eclipse mette a disposizione la possibilità di realizzare i file di configurazione di Struts in modalità grafica, stabilendo le relazioni che ci sono tra le pagine, le action ed i forward. La medesima funzionalità non è offerta per JavaServer Faces, per cui di seguito viene riportata la struttura dei tre moduli che costituiscono la Web application in Struts. 256 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 1 - Struts : Modulo "default" 257 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 2 - Struts : Modulo "admin" 258 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 3 - Struts : Modulo "user" 259 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per quanto riguarda il modulo “default”, si osserva che dalla homepage sia possibile accedere alla funzionalità di login e di registrazione, seguita dall’eventuale acquisto della scheda prepagata. Inoltre, è possibile richiedere l’invio di una mail contenente i dati di accesso in caso di dimenticanza. Si osservi che sono definiti due forward, associati ad una SwitchAction, che permettono di passare al modulo “user” oppure “admin”. Infine, è definita un’eccezione globale nel caso si verifichi un errore di accesso alla base di dati. La struttura del modulo “user” prevede l’accesso alle seguenti funzionalità : - modifica dei dati dell’utente; - acquisto e ricarica della scheda prepagata; - visualizzazione e ricerca dei brani MP3; - gestione delle playlist; - ascolto brani e playlist; - operazione di logout, associata ad una SwitchAction, che permette di tornare al modulo “default”; Anche in questo caso è definita un’eccezione globale nel caso si verifichi un errore di accesso alla base di dati. Infine, l’architettura del modulo “admin” fornisce i seguenti servizi : - modifica dei dati dell’amministratore; - gestione degli utenti; - visualizzazione e ricerca dei brani MP3; - inserimento, modifica ed ascolto dei brani MP3; - operazione di logout, associata ad una SwitchAction, che permette di tornare al modulo “default”; Analogamente agli altri due moduli, è definita un’eccezione globale che viene sollevata qualora si verifichi un errore di accesso alla base di dati. Per completare l’analisi della struttura complessiva della Web application, bisogna descrivere quelli che sono i package contenenti le classi ed il loro ruolo. 260 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Le due implementazioni hanno dei package comuni, in base al fatto che le funzionalità sono sostanzialmente le stesse. Ovviamente, le classi contenute all’interno di essi possono presentare delle differenze. Nel dettaglio, i package comuni sono i seguenti : - accesso : classi relative alle informazioni di accesso dell’utente e dell’amministratore; - brani : classi relative ai brani ed i corrispondenti file MP3; - cartacredito : classe relativa alla carta di credito; - database : classi relative all’accesso alla base di dati. E’ da sottolineare che questo package è perfettamente uguale nelle due implementazioni, a meno della tipologia di eccezioni sollevate nel caso si verifichi un errore di accesso al database. Facendo riferimento al pattern MVC ed in particolare ai sottolivelli del Model, si osserva che questo package costituisce il Data Access Sublayer, completamente indipendente dai sottolivelli che lo precedono; - download : classe relativa all’acquisto e download dei brani; - exception : classi che implementano delle eccezioni personalizzate, estendendo la classe di base Exception. Anche questo package è perfettamente uguale nelle due implementazioni; - listeners : classi che intervengono in particolari situazioni di controllo; - mail : classe per l’invio delle mail; - playlist : classi per la gestione delle playlist; - ruolo : classi che implementano i possibili utilizzatori del sistema; - scheda : classi relative alla scheda prepagata ed alle corrispondenti ricariche; I package specifici dell’implementazione con JavaServer Faces sono i seguenti : - components : classi che implementano dei Custom Components, realizzati per l’interfaccia utente (UI); - validators : classi che implementano alcuni Custom Validators, utilizzati per la validazione dei dati; 261 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Infine, i package relativi all’implementazione con Struts sono : - defaultactions : classi che implementano le action del modulo “default”; - adminactions : classi che implementano le action del modulo “admin”; - useractions : classi che implementano le action del modulo “user”; - commonactions : classi che implementano alcune action comuni a più moduli; - controller : classe che realizza un Controller personalizzato; - formbean : classi che implementano alcuni dei formbean, mentre i restanti sono di tipo dinamico ed dichiarati esclusivamente nel file di configurazione; - plugin : classe che realizza un plug-in per l’inizializzazione di alcuni dati; Un’ ulteriore considerazione riguarda l’internazionalizzazione (I18N), supportata dalla Web application attraverso la definizione di due Resource Bundle. In particolare, ne è previsto uno per la localizzazione di default, ossia l’Italia, e l’altro per un’ulteriore localizzazione supportata, in lingua Inglese. I file che contengono i messaggi sono perfettamente identici nelle due implementazione, a meno di alcuni messaggi relativi agli errori di validazione, che hanno chiavi diverse in relazione al framework utilizzato. Infine, ci sono da motivare le scelte relative alla realizzazione del Model, sostanzialmente diverso tra le due implementazioni. In entrambi i casi, il sottolivello Data Access è completamente indipendente dagli altri e le classi che lo implementano sono perfettamente uguali tra le due implementazioni. Questo ne favorisce la portabilità, qualora si prenda in considerazione l’ipotesi di realizzare la Web application con un ulteriore framework differente. Per quanto riguarda i sottolivelli di Business Logic ed External Interface, si è fatta una scelta diversa per evidenziare le due possibili modalità di approccio che meglio si adattino ai due framework : separazione e fusione. Per quanto riguarda JavaServer Faces, si è utilizzato l’approccio di “fusione”, realizzando i backing beans in modo tale da contenere la business-logic e l’interfacciamento con le classi del framework. Tale scelta ha il vantaggio di non dover realizzare un ulteriore strato di classi che implementi l’External Interface sublayer, il cui scopo sia quello di invocare i metodi sulle classi della business-logic ed utilizzare gli oggetti interni al framework. Lo svantaggio è ovviamente la perdita di 262 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ portabilità, in quanto i backing beans utilizzano delle classi che non sarebbero presenti in framework diversi. Nel caso di Struts, la scelta è in un certo senso obbligatoria, in quanto le classi che implementano le action accedono agli oggetti propri del framework ed invocano i metodi delle classi della business-logic, fungendo automaticamente da interfacciamento. La Business-logic, invece, prevede delle classi completamente indipendenti dal framework, in quanto non hanno l’onere di accedere agli oggetti di quest’ultimo, grazie all’intervento delle action. Il vantaggio è rappresentato dalla portabilità in quanto il medesimo strato di business-logic può essere adottato con framework differenti, lo svantaggio è dato dalla presenza dello strato in più delle action, che comunque è previsto dalla struttura del framework. Si osserva che le scelte hanno i propri pregi e difetti e ciascuna di esse può essere considerata ugualmente valida così come le altre. In molti casi, la propensione verso una di esse può dipendere dal framework e dalle dimensioni dell’applicazione da realizzare. 3. Controller Ciascuno dei due framework fornisce le proprie classi di base che implementano la struttura del Controller di default. Seppur in maniera completamente diversa, è possibile intervenire in Struts e JSF per poter personalizzare tali classi, in modo da aggiungere particolari funzionalità che vanno applicate durante la fase di comunicazione tra Model e View. Nel caso di studio in esame, si è reso necessario l’intervento sul Controller, per poter gestire due situazioni particolarmente delicate : - la gestione delle sessioni non concluse correttamente dell’utilizzatore; - la possibilità di accesso a pagine protette senza la necessaria operazione di login; La prima problematica è stata risolta allo stesso modo nelle due implementazioni, in quanto è più legata alla gestione delle Servlet che non alle classi di uno specifico 263 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ framework. Il secondo problema è stato affrontato in maniera completamente diversa, considerando la differente architettura che assume il Controller in Struts e JSF. 3.1 Gestione delle sessioni Uno dei principali problemi da cui sono afflitte tutte le Web application, è la gestione delle sessioni che non vengono concluse correttamente da un utente. Nel caso specifico, quando l’utilizzatore, utente o amministratore che sia, effettua il login al sistema, vengono memorizzate all’interno della base di dati le informazioni relative all’accesso, ossia la data ed l’ora di ingresso, l’indirizzo IP del client ed il numero di tentativi effettuati. Inoltre, viene settato un flag nel database che indica che l’utente è online, in modo che un tentativo fraudolento di accesso da parte di un secondo utente che disponga dei dati del primo, venga ovviamente bloccato. Nell’effettuare correttamente la fase di logout, le informazioni di accesso vengono aggiornate settando opportunamente la data e l’ora di uscita dal sistema, nonché viene azzerato il flag nel database per segnalare che l’utente è offline. Ci sono moltissime situazioni in cui un utente chiude direttamente il browser, senza completare correttamente il logout. In questo caso è necessario intervenire per ristabilire la coerenza delle informazioni contenute nel database rispetto alla realtà. L’evento di chiusura del browser non può essere intercettato dal server, sul quale è in esecuzione la Web application, ma ovviamente solo dal client. Il linguaggio di scripting lato client, che permette di eseguire elaborazioni su quest’ultimo è ovviamente il JavaScript, il quale però non mette a disposizione la possibilità di intercettare un semplice evento come la chiusura di una finestra del browser, a meno che questa azione non venga eseguita sempre mediante codice. Per questo ed altri motivi, è stato introdotto il concetto di sessione, mediante la quale si stabilisce un legame tra l’utente che utilizza l’applicazione ed il server, su cui quest’ultima è in esecuzione. Alla sessione è associato un concetto di timeout, per cui se non si rileva attività da parte dell’utente fino allo scattare di quest’ultimo, ne viene eseguita l’invalidazione. E’ in corrispondenza di questo evento, che si può intervenire effettuando le operazioni che garantiscano la coerenza delle informazioni nel sistema. Da qui, la necessità di 264 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ realizzare un listener, che allo scadere del timeout di sessione, intervenga per poter permettere allo sviluppatore di effettuare delle elaborazioni necessarie. La realizzazione di un listener di questo tipo è favorita dall’utilizzo dell’interfaccia HttpSessionListener e della classe HttpSessionEvent. La prima mette a disposizione i metodi da eseguire al momento della creazione e distruzione di una sessione, mentre la seconda permette di ricavare un riferimento alla sessione stessa. Per entrambe le implementazioni è stata realizzata la classe InactivityListener, nell’ambito della quale il metodo sessionCreated() ha il compito di settare il timeout di sessione e sessionDestroyed() ha l’onere di cambiare lo stato del flag nel database, per segnalare che l’utente è offline. Quest’ultimo metodo viene appunto invocato nel momento in cui scatta il timeout, in quanto l’utente non sta più generando attività sul server e presumibilmente ha chiuso il browser senza eseguire correttamente il logout. Figura 4 - JSF e Struts : InactivityListener Questo aspetto non può essere considerato un termine di confronto tra i due framework, in quanto le classi in gioco non sono specifiche di Struts e JSF, ma rientrano nell’architettura delle Servlet in generale. Inoltre, la configurazione che specifica l’utilizzo del listener è uguale per entrambi i framework e non prevede l’intervento sui file faces-config.xml e struts-config.xml, ma bensì sul Deployment Descriptor web.xml mediante la seguente dichiarazione : <listener> <listener-class>listeners.InactivityListener</listener-class> </listener> 265 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 3.2 Protezione pagine La seconda problematica in cui è necessario l’intervento sul Controller, riguarda la possibilità che un utilizzatore voglia accedere alle pagine protette dell’applicazione, senza avere eseguito correttamente l’operazione di login. Ovviamente, il sistema deve intercettare un’azione di questo tipo, redirezionando l’utente alla homepage, nella quale è costretto ad eseguire l’accesso. Dall’analisi del problema, si evince che l’intervento deve essere eseguito per ciascuna richiesta che arrivi dal client, in modo da sincerarsi che l’utilizzatore sia correttamente loggato, qualunque sia la pagina a cui vuole accedere, escluse le pagine relative alla registrazione ed alla richiesta dei dati di accesso via mail. I due framework prevedono una risoluzione della problematica completamente differente, data la diversa struttura del Controller, i punti in cui è possibile estendere quest’ultimo ed il differente ciclo di vita a cui è sottoposta una generica richiesta. Per quanto riguarda JavaServer Faces, è noto che la gestione di una richiesta del client viene eseguita attraverso sei fasi distinte. In corrispondenza di ciascuna di esse, può essere utilizzato un listener che permetta di eseguire delle operazioni prima e dopo l’esecuzione della fase stessa. La soluzione adottata prevede la realizzazione della classe LoginCheck che implementa l’interfaccia PhaseListener ed esegue i controlli necessari prima della fase di Render Response. Essa prevede che il metodo afterPhase() sia vuoto, mentre all’interno del metodo beforePhase() ci sia il controllo che l’utilizzatore, utente o amministratore che sia, risulti correttamente loggato oppure la richiesta si riferisca a pagine non protette da login. Nel caso di esito positivo, la navigazione procede normalmente, altrimenti viene ricavato un riferimento al NavigationHandler, il quale permette di spostare il flusso dell’applicazione verso la homepage. Le informazioni relative all’evento verificatesi, ossia l’esecuzione della fase di Render Response, sono contenute nella classe PhaseEvent. 266 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 5 - JSF : LoginCheck La configurazione del listener prevede di associare quest’ultimo all’istanza della classe Lifecycle che si occupa di gestire il ciclo di vita di ogni singola richiesta. <lifecycle> <phase-listener>listeners.LoginCheck</phase-listener> </lifecycle> La soluzione con Struts è completamente diversa, in quanto il ciclo di vita di una richiesta è certamente più semplice e prevede esclusivamente l’intervento della classe RequestProcessor. Per questo motivo, dovendo eseguire delle operazioni di controllo prima che la richiesta venga elaborata, la soluzione prevede di realizzare la classe CustomRequestProcessor che estende la classe base RequestProcessor. In realtà, osservando il codice, si evince che la classe estesa è TilesRequestProcessor che comunque deriva da RequestProcessor. E’ necessaria la derivazione dalla prima, in quanto l’utilizzo di Tiles per il layout prevede l’intervento del proprio Controller. Il metodo che contiene il codice delle operazioni da eseguire è processPreprocess(), il quale viene appunto eseguito prima di iniziare la gestione della richiesta. Se l’utilizzatore risulta correttamente 267 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ loggato, la navigazione prosegue normalmente altrimenti si viene redirezionati alla homepage. Figura 6 - Struts : CustomRequestProcessor E’ da osservare che la classe CustomRequestProcessor è registrata nei moduli “admin” e “user” e non nel modulo “default”, in quanto quest’ultimo non contiene pagine protette, per cui può utilizzare il RequestProcessor di base. In entrambi i file di configurazione struts-config-user.xml e struts-config-admin.xml è presente la seguente dichiarazione : <controller processorClass="controller.CustomRequestProcessor"/> 4. View La realizzazione dell’interfaccia utente (UI) è sicuramente uno degli aspetti nei quali si evidenzia la netta differenza tra JavaServer Faces e Struts, in quanto il primo è strettamente orientato allo sviluppo del Presentation Layer dell’applicazione. Per quanto riguarda la produzione delle pagine JSP, l’implementazione in Struts ha previsto l’utilizzo delle librerie di tag JSTL, in particolare Core e Formatting, oltre alla libreria Struts HTML. La libreria Core sostituisce completamente le librerie Struts Bean e Struts Logic per l’accesso alle proprietà dei bean e la definizione di costrutti condizionali all’interno di ciascuna pagina. La libreria Formatting viene utilizzata soprattutto per la visualizzazione dei messaggi all’utente, prelevandoli dal Resource 268 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Bundle. Infine, la libreria Struts HTML contiene tutti i tag necessari alla costruzione dei form per l’immissione dei dati, così come i tag per la visualizzazione dei messaggi di errore. La formattazione delle pagine ha previsto prevalentemente l’uso di tag HTML, in particolar modo l’utilizzo delle tabelle. L’implementazione in JSF ha previsto l’utilizzo delle due librerie proprie del framework, ossia Core ed HTML, la prima per quanto riguarda aspetti avanzati come l’uso dei validatori mentre la seconda per la costruzione dei form, la visualizzazione dei messaggi e la formattazione del contenuto delle pagine. Con entrambi i framework è stato previsto l’utilizzo di Tiles, del quale è stato necessario importarne la libreria in JSF ma non in Struts, essendo incluso nella distribuzione di quest’ultimo. Inoltre, sfruttando l’enorme potenzialità di JSF, sono stati realizzati dei Custom Components ed il relativo Tag Library Descriptor, in modo da utilizzare questi componenti all’interno delle pagine con un corrispondente tag. Infine, l’implementazione mediante Struts ha previsto l’utilizzo dei formbeans per l’acquisizione dei dati dei form, mentre con JSF è bastato utilizzare i backing beans e le relative proprietà. 4.1 Le librerie Standard Per quanto riguarda le librerie di tag standard che i due framework mettono a disposizione, si può affermare che nessuno dei due prevalga sull’altro. Sia JSF che Struts hanno i propri vantaggi e svantaggi, per quanto concerne i tag che possono essere utilizzati per definire gli elementi costitutivi di ciascuna pagina JSP. E’ da osservare, però, che mentre JSF utilizza solo ed esclusivamente le proprie librerie, Core ed HTML, invece con Struts si preferisce utilizzare le librerie JSTL, Core e Formatting, in luogo di Struts Bean e Struts Logic, considerando il supporto per l’Expression Language. Inoltre, c’è una differenza in termini di configurazione delle librerie da adottare, in quanto JSF non prevede alcun tipo di modifica nel Deployment Descriptor web.xml, mentre Struts prevede l’elenco delle librerie che verranno utilizzate nell’applicazione. 269 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ <taglib> <taglib-uri>/WEB-INF/struts-html</taglib-uri> <taglib-location>/WEB-INF/struts-html.tld</taglib-location> </taglib> ... <taglib> <taglib-uri>/WEB-INF/c</taglib-uri> <taglib-location>/WEB-INF/c.tld</taglib-location> </taglib> ... 4.1.1 I form : contenuto e layout Per quanto riguarda la costruzione dei form, entrambi i framework dispongono di tag equivalenti, ovviamente con sintassi diversa. Un limite di JSF è, però, rappresentato dalla mancanza di un tag che permetta di selezionare un file all’interno del file system locale, per poi tresferirlo sul server. Nel caso di studio in esame, tale funzionalità si è resa necessaria per l’inserimento di un brano all’interno dell’archivio e quindi il caricamento sul server del file MP3 associato. Per ovviare a questo problema, è stato previsto l’utilizzo del tag <t:inputFileUpload> facente parte del progetto Tomahawk. Quest’ultimo prevede una particolare libreria di tag con funzionalità evolute che rientra nell’implementazione di JavaServer Faces realizzata dal gruppo Apache, ossia MyFaces. Ovviamente, oltre al TLD (Tag Library Descriptor) è prevista una libreria di classi che permettono di gestire i corrispondenti tag. Insieme al progetto Tomahawk, si è resa necessaria l’importazione del Commons Project File Upload, sempre del gruppo Apache, contenente le classi che permettono di gestire il caricamento del file sul server. Tale aspetto rappresenta un vantaggio per Struts, in quanto all’interno della libreria standard Struts HTML è previsto il tag <html:file> che fornisce tale funzionalità. Inoltre, il Commons Project File Upload è contenuto all’interno della distribuzione del framework e non deve essere importato dall’esterno così come nella JSF – RI della Sun Microsystems. Figura 7 - Form upload file 270 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Da un punto di vista puramente grafico, si evince che non sussiste alcuna differenza tra le due implementazioni, anche se il procedimento adottato per usufruire di questa funzionalità è ben diverso tra i due framework. Una differenza sostanziale riguarda la definizione della struttura dei form ed il posizionamento degli elementi all’interno della pagina. L’implementazione in Struts ha previsto il semplice utilizzo delle tabelle messe a disposizione del linguaggio HTML, in quanto il framework non dispone di tag particolari per poter svolgere un compito di questo tipo. <html:form styleId="regForm" action="/registrazione.do"> <table> <tr> <td colspan="3" class="formsHeaderText"> <fmt:message key="regHeader"/> </td> </tr> <tr> <td class="columsLabelForm"> <fmt:message key="regUsernameLabel"/> * </td> <td class="columsInputForm"> <html:text property="userName" maxlength="20"/> </td> <td class="errorMessage"> <html:errors property="userName"/> <html:errors property="registrazione"/> </td> ... Ben diversa è l’implementazione in JSF, poichè quest’ultimo introduce il concetto di Panel per la formattazione dei contenuti. Tale concetto è ben noto nell’ambito dello sviluppo delle applicazione desktop tramite le librerie AWT e Swing ed è stato introdotto nelle Web application, come passo verso un’unificazione tra le due tipologie di applicazioni. La libreria JSF HTML fornisce i tag <h:panelGrid> e <h:panelGroup> di cui il primo definisce appunto un Panel, costituito da un fissato numero di colonne, all’interno del quale vengono disposti gli elementi mentre il secondo permette di raggruppare più campi, facendo in modo che nel Panel vengano trattati come un unico elemento. Inoltre, all’interno del Panel è possibile specificare le intestazioni delle colonne, mediante il tag <f:facet>, così come gli stili da applicare, definiti all’interno di un file CSS esterno. 271 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ <h:form id="regForm"> <h:panelGrid columns="3" headerClass="formsHeaderText" columnClasses="columsLabelForm, columsInputForm, columsErrorForm"> <f:facet name="header"> <h:outputText value="#{bundle.regHeader}" /> </f:facet> <h:outputText value="#{bundle.regUsernameLabel} * "/> <h:inputText id="userName" value="#{utente.userName}" required="true" maxlength="20"/> <h:panelGroup> <h:message for="userName" styleClass="errorMessage" /> <h:message for="regForm" id="regError" styleClass="errorMessage"/> </h:panelGroup> ... 4.1.2 Costruzione condizionale dei contenuti Un’ ulteriore notevole differenza riguarda la costruzione condizionale di una pagina, che consiste nel fare in modo che un certo contenuto sia visualizzato o meno a seconda del verificarsi di opportune condizioni. Per quanto riguarda JSF, l’unica possibilità è quella di utilizzare l’attributo rendered di cui è dotato ciascun tag della libreria JSF HTML. Tale attributo può assumere un valore booleano che indica appunto se il tag ed il relativo contenuto debba essere renderizzato o meno. Facendo uso dell’Expression Language e del value-binding, è possibile definire un’espressione nella quale entrino in gioco le proprietà dei backing beans, in modo da esprimere condizioni complesse sulla base delle quali visualizzare o meno il componente. Nell’esempio che segue, il contenuto del Panel viene visualizzato soltanto nel caso in cui l’utente abbia effettuato il login. <h:panelGrid rendered="#{utente.loggedIn}"> ... </h:panelGrid> Il framework Struts, invece, dispone della libreria Struts Logic all’interno della quale ci sono una serie di tag che permettono di definire dei costrutti condizionali e ciclici. Come già detto in precedenza, però, tale libreria non supporta l’Expression Language, per cui un qualsiasi riferimento agli attributi di un bean, deve essere 272 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ eseguito utilizzando attributi distinti dei tag. Per ovviare a questo limite, si utilizzano appunto le librerie JSTL ed in particolare la libreria Core, la quale contiene anche una serie di tag per la costruzione condizionale dei contenuti. Un esempio è caratterizzato dal tag <c:if> che definisce il tipico costrutto if…then dei linguaggi di programmazione. <c:if test="${requestScope.emailInviata == true}"> <div class="Text"><fmt:message key="richiestaOk"/></div> </c:if> ... 4.1.3 DataTable JSF e Cicli JSTL Nell’ambito della Web application realizzata, si è più volte posto il problema di visualizzare un elenco di elementi in forma tabellare, come nel caso dei brani MP3, le playlist e gli utenti registrati. L’implementazione JSF ha previsto l’utilizzo del potentissimo tag <h:dataTable>, il quale permette di specificare la lista degli elementi da visualizzare ed eventualmente le informazioni di formattazione. Inoltre, ciascuna colonna viene definita con il tag <h:column>, all’interno del quale ne viene specificato un’eventuale intestazione ed il relativo contenuto. <h:dataTable value="#{sessionScope.braniMP3}" var="branoMP3" headerClass="headerDataTable" columnClasses="linkDataTable, infoDataTable, infoDataTable, checkBoxDataTable" rowClasses="rowDataTable"> <h:column> <f:facet name="header"> <h:outputText value="#{bundle.titoloLabel}" /> </f:facet> <h:commandLink action="#{branoMP3.visualizzaInfo}"> <h:outputText value="#{branoMP3.titolo}"/> </h:commandLink> </h:column> <h:column> <f:facet name="header"> <h:outputText value="#{bundle.autoreLabel}" /> </f:facet> <h:outputText value="#{branoMP3.autore}" /> </h:column> ... 273 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Dall’esempio si osserva che l’attributo value specifica una variabile di sessione che contiene l’elenco dei brani MP3, mentre l’attributo var definisce il nome della variabile che identifica ciascun elemento all’interno della lista. Nel momento in cui si clicca sul titolo di un brano, per poterne visualizzare le informazioni, l’oggetto corrente specificato nell’attributo var viene trasferito tra i parametri della request, per cui sarà disponibile alla pagina di visualizzazione. Nel caso di Struts, si è reso necessario l’utilizzo del tag <c:forEach> che permette di realizzare un vero e proprio ciclo for all’interno di una pagina. Esso permette di specificare la lista degli elementi da scorrere attraverso l’attributo items ed una variabile che individua l’oggetto corrente attraverso l’attributo var. <table width="500"> ... <c:forEach items="${sessionScope.braniMP3}" var="branoMP3"> <tr class="rowDataTable"> <td class="linkDataTable"> <html:link action="/branoMP3.do?dispatch=visualizza" paramName="branoMP3" paramProperty="id" paramId="idBranoMP3"> <c:out value="${branoMP3.titolo}" /> </html:link> </td> <td class="infoDataTable"> <c:out value="${branoMP3.autore}" /> </td> <td class="infoDataTable"> <c:out value="${branoMP3.album}" /> </td> ... </c:forEach> </table> Dall’esempio, si evince che il tag viene utilizzato all’interno di una tabella per effettuarne una costruzione riga per riga. Il notevole svantaggio che c’è rispetto all’implementazione JSF, è che nel momento in cui si clicca sul titolo di un brano, l’oggetto corrente non viene posto nella request, per cui è possibile superare questo limite inserendo nel link alla pagina di visualizzazione delle informazioni del brano, una query string in cui compaia l’identificativo del brano selezionato. Infine, sarà compito della action invocata, ricavare l’id dalla request e leggere le informazioni del brano dalla base di dati per poi visualizzarle. 274 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 4.2 Tiles Il framework Tiles è stato utilizzato con entrambe le implementazioni per poter dare alla Web application una struttura basata sul concetto di template, in modo che nel momento in cui si renda necessaria una modifica al contenuto delle pagine, questa debba essere eseguita una sola volta e non replicata. Tale framework è incluso nella distribuzione di Struts con il quale si integra perfettamente, soprattutto per quanto riguarda la navigazione, mentre è da importare nel progetto con JSF. La differenza sostanziale sta nella configurazione di Tiles, che è completamente diversa tra un framework e l’altro. Nel caso di JavaServer Faces è necessario modificare il Deployment Descriptor web.xml, che di per se non rappresenta un elemento intrinseco di JSF ma di una generica Web application in Java. Questo mette in evidenza il fatto che per JSF, il framework Tiles è uno strumento del tutto esterno ad esso e va quindi specificato a livello di applicazione. In particolar modo, viene dichiarato attraverso la TilesServlet, che viene avviata subito dopo la FacesServlet e che ha come parametro iniziale il file XML delle definizioni dei layouts. <servlet> <servlet-name>TilesServlet</servlet-name> <servlet-class> org.apache.struts.tiles.TilesServlet </servlet-class> <init-param> <param-name>definitions-config</param-name> <param-value>/WEB-INF/tiles-defs.xml</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> Si osservi che la classe TilesServlet fa parte della distribuzione di Tiles all’interno di Struts. Il fatto che Tiles venga dichiarato mediante una Servlet mette in evidenza che non c’è integrazione con JSF, nel senso che i due framework comunicano fra loro attraverso uno scambio di informazioni tra Servlet e quindi al di fuori dell’architettura dello stesso JavaServer Faces. Il framework Struts prevede la dichiarazione dell’utilizzo di Tiles all’interno del proprio file di configurazione, struts-config.xml, in termini di plug-in. 275 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ <plug-in className="org.apache.struts.tiles.TilesPlugin"> <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/> <set-property property="moduleAware" value="true"/> </plug-in> Con questo tipo di approccio, il TilesRequestProcessor prende il posto del RequestProcessor di Struts dal quale deriva, per cui le richieste pervenute all’applicazione sono gestite direttamente nel framework, sia per quanto riguarda l’elaborazione che la visualizzazione del layout delle pagine. Prendendo in considerazione proprio il layout, le relative componenti sono note come “tiles”, ossia mattonelle, da qui il nome del framework. Inoltre, è stata scelta la struttura maggiormente utilizzata nelle applicazioni, che prevede le seguenti quattro parti fondamentali : un’ intestazione (Header), un fondo pagina (Footer), un menu ed un body. Figura 8 - Layout Web Application 276 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ L’Header ed il Footer sono componenti comuni di tutte le pagine, mentre possono variare il menu, in relazione al tipo di utilizzatore, ma soprattutto il body che rappresenta ogni volta un contenuto differente. Il layout è definito attraverso un unico file e ciascuna pagina dell’applicazione lo utilizza, inserendo di volta in volta le quattro componenti suddette. In questo modo, tutte le pagine hanno la stessa struttura ma con contenuti diversi. Per quanto riguarda l’Header ed il Footer, il vantaggio principale è quello di poter eseguire una modifica su ciascuno di essi una sola volta, senza la necessità di replicarla su tutte le pagine. Lo stesso tipo di vantaggio può essere considerato in relazione al menu, essendo anche quest’ultimo comune a tutte le pagine accessibili da un certo tipo di utilizzatore. Il file delle definizioni, tiles-defs.xml, è leggermente diverso tra le due implementazioni, in quanto entrambi definiscono le parti fisse di ciascuna pagina, ossia il file contente il layout, l’Header ed il Footer, ma si differenziano per la specifica del menu. Infatti, in JSF è stato previsto un unico file di menu, il cui contenuto viene visualizzato o meno a seconda dell’utilizzatore che accede al sistema. <tiles-definitions> <definition name="page.template" path="/templates/mainLayout.jsp"/> <definition name="page.header" path="/templates/header.jsp"/> <definition name="page.footer" path="/templates/footer.jsp"/> <definition name="page.menu" path="/templates/menu.jsp"/> </tiles-definitions> Invece, in Struts sono stati usati tre file distinti contenenti i menu diversi per i tre moduli. <tiles-definitions> <definition name="page.template" path="/templates/mainLayout.jsp"/> <definition name="page.header" path="/templates/header.jsp"/> <definition name="page.footer" path="/templates/footer.jsp"/> </tiles-definitions> Ciascuna delle pagine della Web application utilizza il layout fissato e, per ciascuna definition, specifica il “tile” da inserire all’interno del layout stesso. 277 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Nel caso di JSF, la struttura di ogni pagina è di questo tipo : <tiles:insert definition="page.template"> <tiles:put name="title" value="Titolo Registrazione"/> <tiles:put name="body" value="/context/registrazioneContext.jsp" type="page"/> </tiles:insert> e come si può osservare non viene specificato il menu, oltre all’Header ed al Footer. Con Struts, invece, si ha una cosa del tipo : <tiles:insert definition="page.template"> <tiles:put name="title" value="Titolo Registrazione"/> <tiles:put name="body" value="/context/registrazioneContext.jsp" type="page"/> <tiles:put name="menu" value="/templates/menu.jsp" type="page"/> </tiles:insert> in cui vengono specificati tutti i “tile” da utilizzare, tranne l’Header ed il Footer. 4.3 JavaServer Faces Custom Components Il framework JSF mette a disposizione una potenzialità notevole che consiste nel poter estendere le classi dei componenti della UI, per implementare componenti personalizzati, molto più potenti, da utilizzare nella propria interfaccia utente. Per ciascuno di essi, è necessario eseguire le seguenti fasi : - realizzare la classe che definisce il comportamento del componente, oltre alle fasi di decoding e di encoding se vanno gestite in maniera autonoma; - viceversa, realizzare la classe renderer che si occupi delle operazioni di decode ed encode; - realizzare la classe che costituisce il Tag Handler, per poter gestire il tag associato al componente; - definire le proprietà del tag all’interno di un file TLD (Tag Library Descriptor); 278 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Nella Web application in questione, sono stati realizzati i seguenti componenti : - DateSelectComponent : componente che visualizza tre drop-down list contenente giorni, mesi ed anni, per la selezione di una data. Utilizzato, ad esempio, per l’immissione della data di nascita e della data di scadenza della carta di credito. Inoltre permette di visualizzare un’etichetta associata ed un eventuale messaggio di errore; - DDListComponent : componente che visualizza semplicemente una drop-down list ma con la possibilità di specificarne un’etichetta e visualizzare un eventuale messaggio di errore; - TimeSelectComponent : componente che permetta la selezione di una durata, per esempio del brano MP3, attraverso due drop-down list che contengono minuti e secondi. Inoltre, permette di visualizzare un’etichetta al fianco delle drop-down list; Le classi che implementano i suddetti componenti estendono la classe UIInput e non direttamente la classe di base UIComponent, in modo tale da poter sfruttare il comportamento di default di un componente di input, potenziandone le caratteristiche. Inoltre, i componenti DateSelectComponent e TimeSelectComponent svologono in maniera autonoma le operazioni di deconding e di encoding, per cui non hanno bisogno di un renderer. Viceversa, il componente DDListComponent delega tali operazioni ad una classe renderer corrispondente, implementata estendendo la classe base Renderer. Ovviamente, ciascun componente prevede un proprio Tag Handler realizzato mediante una classe che estende UIComponentTag. 4.3.1 DateSelectComponent L’implementazione di questo componente è stata presa in considerazione, per poter permettere all’utente di specificare in maniera piuttosto banale la propria data di nascita e la data di scadenza della carta di credito, nelle fasi di registrazione e di acquisto o ricarica della scheda prepagata. 279 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 9 – JSF : Esempi d'uso del DateSelectComponent Si può osservare che il componente prevede la visualizzazione di un’ etichetta, una serie di drop-down list ed un eventuale messaggio di errore. Inoltre, la sua introduzione all’interno di una pagina JSP, prevede un unico tag ed attraverso i relativi attributi è possibile specificare il testo dell’etichetta e quali drop-down list visualizzare, tra quelle del giorno, del mese e dell’anno. Nel caso della data di nascita sono necessarie tutte mentre nel caso della scadenza della carta di credito bastano le ultime due. <components:dateSelect id="dataNascita" value="#{utente.dataNascita}" showDay="true" showMonth="true" showYear="true" type="birth"/> L’ulteriore vantaggio è dato dal fatto che una volta selezionata ed inviata la data, tramite un form, il componente non prevede due o tre valori separati che rappresentano giorno, mese ed anno ma bensì un unico valore di tipo Date. Se si fosse pensato di utilizzare i tag delle librerie di base, sarebbero stati necessari due o tre tag <h:selectOneListbox> oppure <h:selectOneMenu> per le drop-down list, più un tag <h:outputText> per l’etichetta. Inoltre, i valori ottenuti sul server sarebbero stati completamente indipendenti e si sarebbero rese necessarie ulteriori elaborazioni per comporli e ricavare una data valida. Questo componente permette di sottolineare le potenzialità di JSF nella costruzione della View ed i notevoli vantaggi che comporta per il Page author. Per quanto riguarda lo sviluppo, la prima classe realizzata è quella relativa al componente vero e proprio, ossia DateSelectComponent. Ques’ultima estende la classe UIInput, in modo da ereditarne il comportamento di base, al quale aggiunge le proprie caratteristiche specifiche. 280 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 10 - JSF : DateSelectComponent, DateSelectTag Gli attributi principali della classe servono per specificare l’etichetta da visualizzare al fianco del componente, quali campi visualizzare tra giorno, mese ed anno ed infine che tipo di data dovrà contenere, nascita o scadenza, in modo da generare l’elenco degli anni in maniera differente. L’operazione di deconding viene eseguita all’interno del componente stesso attraverso il metodo decode(), all’interno del quale vengono ricavati dalla richiesta i parametri che rappresentano i valori del giorno, del mese e dell’anno, i quali vengono utilizzati per creare un oggetto Calendar che rappresenti la data selezionata. Si evince quindi che il componente restituisce un valore di tipo Date, che può essere destinato direttamente ad una proprietà di un backing bean dello stesso tipo. L’operazione di validazione è pressoché superflua, considerando che il componente “guida” l’utente nella scelta della data ed inoltre va sottolineato che, nel caso di data incoerente (es. 31 febbrario 2006), lo stesso oggetto Calendar, in maniera del tutto automatica, ripristina la data valida più vicina. La fase di encoding viene eseguita attraverso dei metodi privati, invocati nel metodo principale encodeBegin(), generando i tag HTML per la visualizzazione dell’etichetta e le drop-down list. Al componente è associato il Tag Handler realizzato mediante la classe DateSelectTag che 281 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ estende UIComponentTag. Essa dispone di tutti gli attributi di cui sarà dotato il tag nella pagina JSP, ma in particolar modo del metodo setProperties(), che ha il compito di caricare i valori di tali attributi che il Page author specifica nel tag, in corrispondenza dei campi della classe DateSelectComponent. Inoltre, è previsto il metodo release(), che permette di rilasciare le risorse allocate per gli attributi, una volta che il componente sia stato renderizzato. Infine, per la descrizione del tag relativo a questo componente così come agli altri, è stato creato un Tag Library Descriptor (components.tld) in cui c’è la descrizione degli attributi associati al tag stesso. <tag> <name>dateSelect</name> <tag-class>components.DateSelectTag</tag-class> <attribute> <name>id</name> <description>Identificativo del component</description> </attribute> <attribute> <name>value</name> <description>Valore del component</description> </attribute> ... E’ ovviamente necessaria la registrazione del componente all’interno del file di configurazione, specificandone la classe che lo implementa. <component> <component-type>DateSelect</component-type> <component-class> components.DateSelectComponent </component-class> </component> 282 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 4.3.2 DDListComponent La realizzazione di questo componente potrebbe sembrare assolutamente superflua, considerando che le librerie di base mettono a disposizione i tag <h:selectOneListbox> e <h:selectOneMenu>, per la visualizzazione della drop-down list, insieme ai tag <f:selectItem> e <f:selectItems>, per specificare gli elementi contenuti nella lista. <components:dropDownList id="stato" value="#{utente.stato}" items="#{itemsBean.stati}" required="true"/> Figura 11 – JSF : Esempio d'uso del DDListComponent I vantaggi principali ottenuti dall’utilizzo del componente sono relativi alla possibilità di visualizzare un’etichetta ed un eventuale messaggio di errore, ma soprattutto di poter specificare la lista degli elementi da visualizzare, non mediante ulteriori tag, ma bensì con un proprio attributo (items). Per quanto riguarda l’implementazione, si è pensato di delegare la fase di deconding e di encoding ad un renderer, in modo da evindeziare il differente approccio rispetto al DateSelectComponent. Tale modalità ha il notevole vantaggio di poter associare ad un medesimo componente più renderers differenti, magari relativi a linguaggi di markup diversi. In questo modo, il comportamento viene definito una sola volta ma le visualizzazioni sono molteplici, in virtù della possibilità di accedere alla Web application con dispositivi client diversi. L’implementazione necessita quindi di tre o più classi : una relativa al componente, una relativa al corrispondente Tag Handler ed una o più classi relative ai renderer. Nel caso di studio in esame, è stato realizzato un unico rendeder per il linguaggio di markup HTML, assumendo che l’applicazione sia accessibile solo ed esclusivamente mediante un browser Web. 283 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 12 – JSF : DDListComponent, DDListTag, DDListRenderer La classe DDListComponent, che estende UIInput, non prevede tutti gli attributi del componente, in quanto alcuni di essi possono essere direttamente gestiti dal Tag Handler, per cui in molti casi si preferisce ometterli nella definizione del componente. Essa prevede il metodo validateValue(), il quale esegue una validazione del valore immesso dall’utente, invocando semplicemente la versione della superclasse. Si osservi che in questo caso, mancano i metodi relativi al deconding ed all’encoding, poiché tali fasi vengono eseguite dal renderer. La classe DDListTag, che deriva da UIComponentTag, specifica tutti gli attributi del componente che saranno presenti nel tag corrispondente ed implementa i tipici metodi setProperties() e release(), per caricare i valori degli attributi nel componente e rilasciare le risorse allocate. Infine, la classe DDListRenderer estende la classe base Renderer per poter implementare il renderer HTML associato al componente. Essa prevede al suo interno il tipico 284 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ metodo encodeBegin(), il quale utilizza dei metodi privati per la visualizzazione del componente. Inoltre, è stato eseguito l’overidding del metodo getConvertedValue(), per poter gestire all’interno del renderer stesso la conversione del valore selezionato. In molte situazioni, tale operazione non è strettamente necessaria, in quanto è il framework che esegue le conversioni in maniera automatica. In questo caso, si rende necessaria l’operazione di conversione in quanto il componente viene utilizzato per visualizzare sia liste con valori numerici (es. importo scheda prepagata) che stringhe (es. stato). La descrizione del tag è contenuta all’interno del file components.tld e per quanto riguarda la configurazione, è necessario registrare sia il componente che il renderer. Quest’ultimo va tipicamente inserito all’interno di un Render Kit, in base al dispositivo client di accesso, ma in questo caso è stato registrato in quello di “default” relativo al linguaggio HTML. <renderer> <component-family>DropDownList</component-family> <renderer-type>DropDownListRenderer</renderer-type> <renderer-class>components.DDListRenderer</renderer-class> </renderer> 4.3.3 TimeSelectComponent Questo componente è stato realizzato per uno scopo ben preciso, ossia permettere all’amministratore di specificare la durata di un brano MP3, semplicemente selezionando i valori di minuti e secondi mediante due drop-down list. <components:timeSelect id="durata" value="#{branoMP3.durata}"/> Figura 13 – JSF : Esempio d'uso del TimeSelectComponent Il tipico vantaggio è sempre quello di evitare l’utilizzo di più tag, soprattutto per la visualizzazione delle due drop-down list e del loro contenuto. Inoltre, il componente restituisce automaticamente un oggetto di tipo Time, in luogo di due valori distinti 285 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ che rappresentano i minuti ed i secondi. Per quanto riguarda l’implementazione, esso è stato realizzato in modo tale da gestire in maniera autonoma le fasi di decondig e di encoding. Figura 14 – JSF : TimeSelectComponent, TimeSelectTag La classe TimeSelectComponent estende UIInput e definisce i metodi necessari per le operazioni di decode e di encode. In particolare, il metodo decode() ricava dalla request i parametri relativi ai minuti ed i secondi selezionati e li compone in modo da ricavare un unico oggetto Time. La fase di encoding, ovviamente, esegue l’operazione inversa, qualora si debbano visualizzare le informazioni di un brano MP3, tra cui appunto la durata. Inoltre, lo stesso metodo genera automaticamente la lista dei valori da poter selezionare. Il Tag Handler è realizzato mediante la classe TimeSelectTag, che estende UIComponentTag, la quale contiene tutti gli attributi previsti nel tag ed i tipici metodi per gestire il caricamento dei valori di questi ultimi all’interno del componente e per rilasciare le risorse allocate. Il tag associato è ovviamente descritto all’interno del file components.tld ed il componente è registrato all’interno del file di configurazione. 286 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 15 - JSF : Custom Components 4.3.4 …e Struts ? Il framework Struts non mette assolutamente a disposizione un meccanismo che permetta di implementare dei componenti personalizzati come in JSF. La realizzazione dell’interfaccia utente può essere svolta utilizzando tutti quelli che sono i tag della libreria Struts HTML, senza possibilità di estensione. Tale limite si è evidenziato soprattutto per alcuni campi necessari nei form, come ad esempio la data di nascita, la data di scadenza della carta di credito e la durata di un brano MP3. Con JavaServer Faces, queste necessità particolari sono state risolte attraverso la realizzazione di corrispondenti Custom Components, mentre nel caso di Struts, si è preferito utilizzare dei semplici campi di testo. Figura 16 - Struts : Esempio campo di input di una data 287 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Si sarebbe potuto pensare di servirsi dei tag relativi alle drop-down list, ma in quel caso sarebbe stato necessario far gestire alla action ricevente, l’operazione di comporre i valori separatamente ricavati dalla request per ottenere un unico valore corrispondente. L’utilizzo di un semplice campo di testo ha permesso di avvalersi di una particolare regola di validazione, prevista nel plug-in Validator, relativa alle date. In questo modo, si può comunque effettuare un opportuno controllo per verificare che l’utente abbia immesso una data valida. 4.4 I formbean di Struts In riferimento all’acquisizione dei dati contenuti nei form ed inviati dall’utente, il framework Struts introduce il concetto di formbean, il quale altro non è che una particolare classe le cui proprietà sono destinate a contenere i valori dei campi del form stesso. Nel momento in cui viene inviata una richiesta, è compito del RequestProcessor popolare il formbean ed effettuare la validazione dei dati in esso contenuti, per poi passarlo alla action che dovrà occuparsi di gestire la richiesta. In generale, un formbean può essere di due tipi : - statico : realizzato attraverso una classe che estende la classe ActionForm oppure una delle sue derivate e poi dichiarato nel file di configurazione; - dinamico : dichiarato direttamente all’interno del file di configurazione, senza la necessità di implementare una classe corrispondente; Tipicamente, si preferisce utilizzare i formbean dinamici, anche se sussistono alcune situazioni in cui si rende necessaria l’implementazione delle classi per quelli statici. Anche nel caso della Web application realizzata, si è fatto uso di entrambe le tipologie di formbean, dinamici lì dove possibile e statici lì dove fossero necessari per un certo motivo. 288 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 4.4.1 Login, Registrazione e Richiesta password La funzionalità di Login prevede un semplice form con due campi, all’interno dei quali specificare lo Username e la Password. Figura 17 - Form di Login Ad esso è stato associato un formbean di tipo statico, implementando la classe LoginForm che estende la classe base ActionForm. E’ stata scelta questa soluzione, in quanto facendo riferimento alla fase di registrazione, quando l’utente termina la procedura, viene eseguita un’operazione di login automatico per cui era necessario riempire il form di login direttamente dal codice. Attraverso la realizzazione della classe LoginForm, è stato possibile istanziare un oggetto di questo tipo, popolarlo con i dati di accesso ed invocare la action che si occupasse dell’azione di login. Figura 18 - Struts : LoginForm L’implementazione è notevolmente semplice, in quanto le proprietà della classe non sono altro che i campi previsti nel form : userName e password. Non è stato effettuato l’overriding del metodo validate(), in quanto la validazione dei dati immessi non è necessaria per due motivi : 289 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - Lo username e la password possono contenere caratteri alfanumerici qualsiasi; - Se non viene immesso alcun valore, si impedisce semplicemente l’accesso; Infine, il formbean è stato registrato all’interno del modulo “default”, dal quale è accessibile la funzionalità di login, specificandone il nome e la classe che lo implementa. <form-bean name="loginForm" type="formbean.LoginForm"/> La funzionalità di Registrazione prevede in primo luogo il form per i dati di accesso che sono indispensabili e poi eventualmente il form di acquisto della scheda prepagata, qualora l’utente voglia avvalersi di questo servizio. Poiché quest’ultima funzionalità è disponibile anche per un utente già registrato, ma non ancora in possesso di una scheda, verrà trattata successivamente. Figura 19 - Form di Registrazione Ad esso è associato un formbean realizzato mediante la classe RegistrazioneForm, la quale estende ValidatorForm e non semplicemente ActionForm. L’implementazione della classe si è resa necessaria per una sorta di “bug” documentato di Struts, dovuto alla presenza della checkbox con la quale l’utente può scegliere se proseguire la procedura di registrazione, acquistando anche la scheda prepagata. Dalla documentazione ufficiale di Struts si legge “ATTENZIONE : per poter acquisire correttamente il valore di una chechbox non selezionata, l’ActionForm associato al 290 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ form in questione deve contenere, all’interno del metodo reset(), un’istruzione che setti a false il valore della checkbox”, da qui il motivo della classe. Inoltre, quest’ultima deriva da ValidatorForm, in quanto la validazione dei dati viene eseguita attraverso il plug-in Validator e non mediante il metodo validate(). Figura 20 - Struts : RegistrazioneForm La classe realizzata ha delle proprietà che corrispondono ai campi del form ed il metodo reset() per il motivo suddetto. Ovviamente, il formbean è registrato nel file di configurazione allo stesso modo del bean precedente. Infine, la funzionalità di richiesta della password prevede che l’utente inserisca molto semplicemente l’indirizzo email con cui si è registrato per poter ricevere una mail con i dati di accesso. Figura 21 - Form di Richiesta Username/Password In questo caso è bastato realizzare un formbean dinamico semplicemente dichiarando nel file di configurazione e specificandone le proprietà relative. <form-bean name="richiediPasswordForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="email" type="java.lang.String"/> </form-bean> 291 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Come si può osservare, il formbean sarà un’istanza della classe DynaValidatorForm essendo dinamico e poiché su di esso verrà eseguita la validazione attraverso il plugin Validator. Inoltre, viene specificata l’unica proprietà che ne fa parte con il relativo tipo. 4.4.2 Acquisto/Ricarica scheda prepagata La funzionalità di acquisto della scheda prepagata è accessibile in fase di registrazione oppure in un qualsiasi momento, nel caso di un utente registrato che non ha deciso di usufruire di questo servizio in precedenza. Essa prevede un form all’interno del quale l’utente deve immettere i propri dati personali, l’importo della scheda e le informazioni della carta di credito. Figura 22 - Form di Acquisto scheda prepagata 292 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Il form è associato ad un formbean di tipo dinamico, dichiarato nei file di configurazione del modulo “default” e “admin”, da cui la funzionalità è accessibile. Tale formbean è del tipo DynaValidatorForm, tenendo sempre conto che la validazione viene eseguita mediante il plug-in Validator. Inoltre, le sue proprietà corrispondono perfettamente ai campi del form. <form-bean name="acquistoSchedaForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="dataScadenzaCC" type="java.lang.String"/> <form-property name="dataNascita" type="java.lang.String"/> <form-property initial="M" name="sesso" type="java.lang.String"/> ... </form-bean> La funzionalità di ricarica della scheda prepagata prevede un semplice form all’interno del quale l’utente deve specificare l’importo della ricarica e le informazioni della carta di credito. Figura 23 - Form di Ricarica scheda prepagata Anche in questo caso è stato definito un formbean dinamico, del tipo DynaValidatorForm, dichiarato solo ed esclusivamente nel file di configurazione del modulo “user”. 293 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 4.4.3 Modifica dati utente ed amministratore Attraverso la corrispondente funzionalità, l’utente ha la possibilità di modificare i propri dati di accesso ed i dati anagrafici, nel caso abbia a disposizione una scheda prepagata della quale ne abbia effettuato l’acquisto. Il form previsto è una sorta di unione tra il form di registrazione e quello per l’acquisto di una scheda prepagata, a meno di un campo in più che prevede l’immissione della conferma della password scelta. Un parte di esso è visibile nella figura seguente. Figura 24 - Frammento del Form di Modifica dati Utente Il formbean ad esso associato è dinamico e del tipo DynaValidatorForm e le sue proprietà corrispondono ai campi del form. Esso è dichiarato sempre allo stesso modo nel file di configurazione del modulo “user”. Un’analoga funzionalità è prevista per l’amministratore, il quale però ha la possibilità di modificare solo ed esclusivamente la password, non essendo associate ad esso ulteriori informazioni. Il form è perfettamente uguale a quello previsto per l’utente ed il formbean associato è comunque di tipo dinamico, ma dichiarato ovviamente nel file di configurazione del modulo “admin”. 4.4.5 Visualizzazione e ricerca dei brani La visualizzazione dell’elenco dei brani presenti in archivio è prevista sulla base di due modalità differenti, di cui la prima permette di scegliere un genere 294 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ musicale mentre la seconda di impostare dei criteri di ricerca più precisi basati sul titolo, l’autore e l’album. Il form della prima modalità è banale in quanto è caratterizzato da un'unica dropdown list all’interno della quale selezionare il genere musicale a cui si è interessati. Figura 25 - Form di Visualizzazione brani MP3 per genere Il formbean associato è del tipo DynaValidatorForm ed ha ovviamente un’unica proprietà associata al campo del form. La seconda modalità di visualizzazione prevede un form leggermente più complesso, in quanto, oltre al genere è possibile impostare criteri di ricerca specifici sulle altre informazioni del brano. Figura 26 - Form di Ricerca brani MP3 Esso prevede un formbean dinamico con le proprietà genere, titolo, autore ed album relative ai campi del form. Esso è registrato correttamente sia nel modulo “admin” che “user”. <form-bean name="ricercaBraniMP3Form" type="org.apache.struts.action.DynaActionForm"> <form-property name="genere" type="java.lang.String"/> <form-property name="titolo" type="java.lang.String"/> <form-property name="autore" type="java.lang.String"/> <form-property name="album" type="java.lang.String"/> </form-bean> 295 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Una volta eseguita la richiesta di visualizzazione oppure terminata la ricerca, se ci sono dei brani che soddisfano i criteri impostati, viene visualizzato un elenco degli stessi. Nel caso in cui l’utilizzatore sia l’amministratore oppure un utente che ha delle playlist create in precedenza, al fianco dei brani sono visualizzate delle checkbox che permettono all’amministratore di selezionare i brani da eliminare ed all’utente di selezionare i brani da aggiungere ad un certa playlist. Figura 27 - Form di Visualizzazione ed eliminazione brani MP3 dall'archivio Figura 28 - Form di Visualizzazione ed inserimento dei brani MP3 in una playlist Ovviamente, le checkbox devono far parte di un form che verrà inviato alla action che dovrà occuparsi dell’elaborazione. Avendo utilizzato il tag <html:multibox> per la generazione delle checkbox, il formbean è stato implementato mediante la classe BraniMP3Form che estende ActionForm, che ha le seguenti proprietà : - idBraniMP3Selezionati : un array di stringhe che contiene gli identificativi dei brani scelti, riempito automaticamente dal framework sulla base delle checkbox selezionate; - idPlaylist : contiene l’identificativo della playlist scelta dall’utente all’interno della quale immettere i brani selezionati; 296 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Ovviamente, la prima proprietà è utilizzata sia nel caso di accesso dell’amministratore che dell’utente, mentre la seconda solo ed esclusivamente in quest’ultimo caso. Figura 29 - Struts : BraniMP3Form Il formbean è registrato nei file di configurazione del modulo “admin” e “user”. 4.4.6 Inserimento/Modifica brani Per effettuare l’inserimento di un nuovo brano all’interno dell’archivio, l’amministratore ha a disposizione un form nel quale poter inserire le informazioni del brano e selezionare il file MP3 di cui eseguirne il caricamento sul server. Per quanto riguarda la modifica, tale funzionalità è disponibile nell’interfaccia che visualizza le informazioni di un certo brano e da essa è possibile eseguire anche le operazioni di eliminazione ed ascolto. Il form è praticamente uguale al precedente, salvo il fatto che non prevede il campo di selezione del file MP3. Figura 30 - Form di Inserimento brano MP3 297 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 31 - Form di Modifica brano MP3 I formbean associati ai due form sono sostanzialmente simili a meno del fatto che quello relativo alla modifica del brano, prevede la proprietà dispatch, la quale è associata ad un campo hidden che ovviamente non è visibile. La necessità di questa proprietà è legata alla presenza di due pulsanti di submit, “modifica” ed “elimina”, all’interno di quest’ultimo. Tali pulsanti non avviano due action differenti, ma come si vedrà di seguito è definita un’unica action che contiene una serie di metodi che eseguono le possibili azioni su un brano MP3, per cui la proprietà dispatch è necessaria per distinguere quale metodo invocare. Alla pressione di uno dei due pulsanti, viene assegnato un valore appropriato a tale proprietà e quest’ultima viene inserita nella query string. Il RequestProcessor, sulla base di tale valore, avvia l’esecuzione del metodo corrispondente della action invocata. Viceversa, al pulsante di “ascolto” non è legata alcuna action, in quanto la sua pressione comporta l’apertura di una finestra pop-up all’interno della quale viene caricato il player MP3 ed avviato l’ascolto del brano. I formbean sono ovviamente dinamici e dichiarati unicamente nel file di configurazione del modulo “admin”. 4.4.7 Gestione Playlist Per quanto riguarda la creazione di una playlist, è previsto un semplice form costituito da un unico campo all’interno del quale specificarne il nome. 298 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 32 - Form di Creazione playlist Il formbean associato è di tipo statico ed è stato implementato mediante la classe PlaylistForm che deriva da ValidatorForm, per poter effettuare la validazione sul nome della playlist. Figura 33 - Struts : PlaylistForm E’ inoltre prevista un’ ulteriore proprietà, idPlaylistSelezionate, la quale viene utilizzata per la selezione delle playlist da cancellare. Infatti, l’utente ha la possiblità di visualizzare l’elenco di tutte le playlist create in precedenza ed , attraverso delle checkbox, può selezionare quali playlist eliminare. Figura 34 - Form di Visualizzazione ed eliminazione delle playlist Così come nel caso della gestione dei brani, il framework riempie automaticamente l’array di stringhe idPlaylistSelezionate, con gli identificativi delle playlist scelte attraverso le checkbox selezionate. Il formbean in questione, quindi, viene utilizzato 299 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ nella gestione delle due funzionalità suddette ed è registrato ovviamente solo nel modulo “user”. 4.4.8 Gestione Utenti L’amministratore dispone di semplici funzionalità per poter gestire gli utenti registrati. In particolare, è possibile visualizzare l’elenco degli utenti e selezionare quelli da cancellare attraverso delle checkbox. Figura 35 - Form di Visualizzazione ed eliminazione degli utenti Per poter eseguire l’operazione di eliminazione, viene invocata una particolare action, la quale riceve un formbean implementato attraverso la classe UtentiForm che deriva da ActionForm e che ha una proprietà, idUtentiSelezionati, che contiene l’elenco degli identificativi degli utenti scelti per la cancellazione. Figura 36 - Struts : UtentiForm Inoltre, è prevista la proprietà dispatch, in quanto la action che si occupa della cancellazione permette di eseguire tutte le operazioni possibili sull’oggetto Utente. Per questo motivo, assegnando il valore opportuno a questa proprietà all’interno della 300 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ query string, è possibile segnalare al RequestProcessor, quali dei metodi della action debba essere avviato. Infine, il formbean è registrato unicamente nel modulo “admin”. 4.4.9 … e JavaServer Faces ? Rispetto a Struts, il framework JSF ha una gestione completamente diversa dai form. Dal punto di vista puramente grafico, questi ultimi sono sostanzialmente identici a meno della possibilità di utilizzare i Custom Components che sono stati realizzati con JavaServer Faces. Per quanto riguarda le operazioni di acquisizione dei dati, quest’ultimo non prevede assolutamente il concetto di formbean. I campi di ciascun form sono associati alle proprietà dei backing beans, così come la pressione di un bottone per l’invio dei dati non prevede l’esecuzione di una action esterna ai bean, ma bensì di uno dei metodi del backing bean stesso. Tutto ciò ha il vantaggio di non dover utilizzare un oggetto intermedio, il formbean appunto, che abbia il compito del trasferimento dei dati dall’utente alla elaborazione da eseguire. Ad esempio, considerando la funzionalità di login, essa prevede il medesimo form dell’implementazione in Struts, con la differenza che i valori di Username e Password sono destinati all’interno delle proprietà del backing bean ruolo della classe Ruolo ed alla pressione del pulsante, viene invocato il metodo login() dell’oggetto. <h:form id="loginForm"... ... <h:outputText value="#{bundle.menuUsernameLabel}" /> <h:inputText id="userName" value="#{ruolo.userName}"/> <h:outputText value="#{bundle.menuPasswordLabel}" /> <h:inputSecret id="password" value="#{ruolo.password}"/> <h:outputText value="" /> <h:commandButton action="#{ruolo.login}" value="#{bundle.menuButtonLoginLabel}" styleClass="buttonForm"/> </h:form> In questo modo, i dati sono immediatamente disponibili e l’elaborazione da eseguire su di essi si trova all’interno della classe stessa. Il backing bean è dichiarato all’interno del file di configurazione, specificandone anche l’ambito di visibilità (scope). 301 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ <managed-bean> <managed-bean-name>ruolo</managed-bean-name> <managed-bean-class>ruolo.Ruolo</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> Questo tipo di approccio è utilizzato in tutte le altre funzionalità e può essere considerato un vantaggio a favore del framework JavaServer Faces. 4.5 Player MP3 Il Player MP3 adottato per l’ascolto dei brani MP3 e delle playlist è implementato mediante un’applet, JLGUIApplet, disponibile gratuitamente su Internet. Attraverso il file di inizializzazione jlgui.ini, esso mette a disposizione la possibilità di modificare una serie di opzioni tra cui, in particolre, il percorso del file MP3 da ascoltare oppure del file M3U che contiene l’elenco dei brani, nel caso si tratti di una playlist. L’ascolto di un singolo brano prevede la definizione del percorso del file MP3 associato all’interno del file system mentre nel caso in cui l’utente desideri ascoltare una propria playlist, l’applicazione non fa altro che leggere la composizione della stessa dalla base di dati e creare un file M3U con i percorsi dei brani in modo da renderli accessibili al player. Dal punto di vista dell’interfaccia utente, viene aperta una finestra pop-up all’interno della quale viene caricata l’applet. Da qui, l’utente ha la possibilità di interagire con quest’ultima modificando la sequenza dei brani, utilizzando i controlli di volume e di bilanciamento, nonché di sfruttare l’equalizzatore messo a disposizione. E’ da osservare che, con questo approccio, l’utente ha comunque la possibilità di proseguire la navigazione all’interno del sistema, proseguendo l’ascolto della propria playlist. L’unico limite del player è che il percorso che specifica la locazione del file da ascoltare, deve fare riferimento al medesimo host con cui si accede alla Web application e specificato nella barra degli indirizzi del browser. 302 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 37 - Player MP3 4.6 Internazionalizzazione (I18N) Attraverso il meccanismo dell’Internazionalizzazione (I18N), è possibile visualizzare il testo presente nella Web application in lingue diverse, in relazione alla localizzazione geografica da cui si collega l’utente oppure sulla base delle impostazioni della lingua del browser. Nel caso dell’applicazione realizzata, si è previsto il supporto per la lingua italiana, considerata di default, e per la lingua inglese. In questo modo, senza la necessità di dover sviluppare due volte la medesima Web application, è possibile comunque garantire l’utilizzo di quest’ultima a persone di lingua differente. Per quanto riguarda questo aspetto, i framwork Struts e JSF sono perfettamente identici, in quanto forniscono tale supporto allo stesso modo, anche in virtù del fatto che questo concetto è proprio del linguaggio Java. In entrambe le implementazioni sono stati realizzati due Resource Bundle contenenti i messaggi da visualizzare nelle due lingue. Ciascuno di essi è costituito da una serie di coppie “key-message”, attraverso le quali è possibile visualizzare un certo messaggio semplicemente specificando la chiave ad esso associata. 303 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ ... regStatoLabel=Stato : regTelefonoLabel=Telefono : regDataNascitaLabel=Data di nascita : ... ResourceBundle IT ... regStatoLabel=State : regTelefonoLabel=Phone Number : regDataNascitaLabel=Date of birth : ... ResourceBundle EN Come si può osservare, in entrambi i file (con estensione properties) le chiavi sono perfettamente identiche mentre i messaggi sono in lingua diversa. Inoltre, i file stessi hanno praticamente il medesimo nome, ossia ApplicationResources ed ApplicationResources_en, a meno del file in lingua inglese che ha il suffisso “en”. Tipicamente, il file che non ha alcun suffisso è quello relativo alla lingua di default mentre i file associati alle altre lingue devono avere lo stesso nome del primo ma seguito da un suffisso che identifica la lingua. Entrambi i framework hanno previsto la registrazione dei Resource Bundle, con una leggera differenza. Nel caso di JSF è stato necessario specificare la localizzazione (Locale) di default e quella supportata, oltre al nome del file contenente i messaggi. <application> <locale-config> <default-locale>it</default-locale> <supported-locale>en</supported-locale> </locale-config> <message-bundle>ApplicationResources</message-bundle> </application> Invece, Struts ha previsto semplicemente la dichiarazione del Resource Bundle. <message-resources parameter="ApplicationResources"/> Per quanto riguarda l’utilizzo dell’internazionalizzazione all’interno delle pagine dell’applicazione, le librerie di entrambi i framework mettono a disposizione un tag che permette di specificare quale sia il bundle dal quale prelevare i messaggi. 304 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ JavaServer Faces prevede l’utilizzo del tag <f:loadBundle> in cui l’attributo basename specifica il nome del Resource Bundle mentre var permette di assegnargli un nome mediante il quale farvi riferimento all’interno della pagina JSP. <f:loadBundle basename="ApplicationResources" var="bundle" /> Nell’implementazione in Struts è stato utilizzato il tag <fmt:setBundle> della librearia Formatting JSTL, il quale prevede l’unico attributo basename per specificare il nome del bundle. Per poter individuare un messaggio da visualizzare, viene utilizzato un approccio differente. Con JSF si utilizza l’Expression Language e la tipica “dot notation”, specificando il nome del bundle e la chiave del messaggio da visualizzare. <h:outputText value="#{bundle.regAnagraficaHeader}" /> Viceversa, con Struts non si fa uso dell’EL ma attraverso un particolare tag si specifica semplicemente la chiave associata al messaggio. <fmt:message key="regAnagraficaHeader"/> Utilizzando il medesimo strumento, seppure con sintassi leggermente diverse, dal punto di vista grafico le due implementazioni forniscono il medesimo risultato. Figura 38 - Esempio di Form con localizzazioni differenti Un limite legato all’Internazionalizzazione riguarda il fatto che, nel caso in cui la navigazione tra le pagine non sia definita attraverso collegamenti ipertestuali ma attraverso elementi grafici, non è possibile visualizzare un elemento al posto di un altro in base alla differente lingua di accesso. I Resource Bundle supportano il 305 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ contenuto testuale e non eventuali immagini che costituisco i bottoni per la navigazione. 5. Validazione La validazione dei dati immessi nei form da parte dell’utente è uno degli aspetti su cui si differenziano maggiormente i framework a confronto. La differenza sostanziale risiede nel fatto che JSF mette a disposizione un proprio meccanismo per la validazione e per la sua estensione mentre Struts, nella maggior parte dei casi, si affida al plug-in Validator anche se è previsto un metodo di validazione interno al framework. Le scelte fatte nelle due implementazioni sono fondate sulla riusabilità e sulla rapidità di sviluppo, sfruttando lì dove possibile tutti gli strumenti a disposizione. 5.1 JavaServer Faces Custom Validators JavaServer Faces mette a disposizione soltanto tre validatori standard che permettono di valutare se un certo valore ha una certa lunghezza entro un minimo ed un massimo prefissati oppure se il medesimo valore rientra in un certo range. Per quanto riguarda l’obbligatorietà di un campo, quest’ultima viene specificata mediante l’attributo required del tag associato al componente in questione. Un esempio di obbligatorietà di un campo può essere la richiesta dello Username in fase di registrazione, come mostrato in figura. Figura 39 - JSF : Esempio di campo obbligatorio Per quanto riguarda il controllo che un valore di un campo debba avere una lunghezza minima prefissata, si può fare riferimento al tag <f:validateLength> utilizzato in fase di registrazione per la richiesta della password, per la quale è stata fissata una lunghezza minima di 5 caratteri alfanumerici. 306 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ <h:inputSecret id="password" value="#{utente.password}" required="true" maxlength="15"> <f:validateLength minimum="5"/> </h:inputSecret> Figura 40 - JSF : Esempio di ValidateLength L’ultimo validatore standard, relativo al controllo che un certo valore rientri in un range fissato, è stato utilizzato nella fase di inserimento di un nuovo brano MP3 in archivio nell’ambito della specifica del costo di quest’ultimo. Tale validatore è fornito dal framework attraverso i tag <f:validateDoubleRange> e <f:validateLongRange>, sulla base del tipo di dato del valore da validare. <h:inputText id="costo" value="#{branoMP3.costo}" required="true" size="5"> <f:validateDoubleRange minimum="0.0"/> </h:inputText> Figura 41 - JSF : Esempio di ValidateDoubleRange Considerando il numero limitato di controlli standard che possono essere eseguiti, il framework mette a disposizione due meccanismi per la realizzazione di ulteriori validatori. Il primo consiste nel realizzare un metodo che esegua la validazione all’interno di un backing bean, il secondo prevede lo sviluppo di una classe che implementi l’interfaccia Validator, alla quale può poi essere associato o meno un tag. Tipicamente la prima soluzione è strettamente limitata all’uso dello specifico backing bean e non permette di utilizzare il validatore su più componenti che prevedono lo stesso tipo di controllo, a meno di non effettuare un copia-incolla del metodo di validazione nei vari backing beans. Per questo motivo, si è preferita la seconda soluzione grazie alla quale un unico validatore può essere facilmente associato a più componenti. 307 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ L’implementazione in JSF ha previsto lo sviluppo dei tre seguenti validatori : - EmailValidator : permette di valutare se il formato di un indirizzo email sia o meno corretto; - SeqCifreValidator : per valutare se un certo valore è rappresentato da una sequenza di cifre; - TelefonoValidator : per verificare se un numero di telefono abbia un formato valido, del tipo prefisso-numero; 5.1.1 EmailValidator Questo validatore viene utilizzato nella fase di registrazione di un utente oppure nel momento in cui quest’ultimo richiede l’invio di una mail con i propri dati di accesso, specificando il proprio indirizzo. La classe EmailValidator implementa l’interfaccia Validator della quale esegue l’overriding del metodo validate() per valutare se l’indirizzo email immesso dall’utente abbia un formato corretto. Figura 42 - JSF : EmailValidator 308 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per poter effettuare il controllo, all’interno del metodo validate() viene utilizzato lo strumento delle Regular Expression (Espressione Regolare) fornito dal linguaggio Java. In particolare, si specifica quale sia il pattern a cui deve essere conforme un indirizzo email per essere considerato corretto e si valuta se il valore immesso dall’utente corrisponda ad esso. ... Pattern mask = Pattern.compile("\\w+@{1}\\w+\\.{1}\\w+"); Matcher matcher = mask.matcher(value.toString()); // se il valore non corrisponde al pattern if (!matcher.matches()){ ... ... throw new ValidatorException(msg); } ... Si osserva che se il valore immesso dall’utente non è conforme al pattern specificato, viene sollevata un’ eccezione la quale contiene il messaggio da visualizzare all’utente. Tale messaggio viene prelevato dal Resource Bundle. Il validatore ha ovviamente previsto una registrazione all’interno del file di configurazione, così come tutti gli elementi personalizzati che si creano con JSF. <validator> <validator-id>emailValidator</validator-id> <validator-class>validators.EmailValidator</validator-class> </validator> Per quanto riguarda il suo utilizzo all’interno di una pagina JSP, si è preferito non realizzare un tag appropriato ma di utilizzare il tag standard messo a disposizione da JSF, ossia <f:validator>, il quale prevede l’attributo validatorId che permette di specificare l’identificativo del validatore da utilizzare, così come è stato registrato. <h:inputText id="email" value="#{utente.email}" required="true" maxlength="50"> <f:validator validatorId="emailValidator"/> </h:inputText> 309 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ L’immissione da parte dell’utente di un indirizzo con un formato errato, prevede una segnalazione del tipo mostrato in figura. Figura 43 - JSF : Esempio d'uso dell'EmailValidator 5.1.2 SeqCifreValidator Nell’ambito dell’applicazione realizzata, ci sono una serie di campi dei form che prevedono l’immissione di un valore caratterizzato solo ed esclusivamente da una sequenza di cifre, come ad esempio il C.A.P ed il numero della carta di credito. Per verificare che l’utente immetta un valore corretto, è stato realizzata la classe SeqCifreValidator che implementa l’interfaccia Validator. Figura 44 - JSF : SeqCifreValidator All’interno del metodo validate() viene definito il pattern a cui deve essere conforme un valore caratterizzato esclusivamente da cifre e viene sollevata un’eccezione 310 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ qualora il controllo abbia esito negativo, in modo da visualizzare un messaggio di errore all’utente. ... Pattern mask = Pattern.compile("\\d+"); Matcher matcher = mask.matcher(value.toString()); // se il valore non corrisponde al pattern if (!matcher.matches()){ ... ... throw new ValidatorException(msg); } ... Infine, eseguita la registrazione nel file di configurazione, il validatore viene utilizzato mediante il tag <f:validator>. <h:inputText id="cap" value="#{utente.cap}" maxlength="5" size="5"> <f:validator validatorId="seqCifreValidator" /> </h:inputText> Nel caso in cui il controllo abbia un esito negativo, viene visualizzato un opportuno messaggio di errore prelevato dal Resource Bundle. 5.1.3 TelefonoValidator Generalmente, il valore di un campo che deve rappresentare un numero di telefono è caratterizzato da un formato particolare del tipo “prefisso-numero”. Nel caso dell’applicazione realizzata, una situazione di questo tipo si verifica nella fase di registrazione di un utente o comunque nell’acquisto di una scheda prepagata, in cui tra i dati facoltativi c’è anche la richiesta del numero di telefono. Per poter garantire che l’utente immetta un valore valido, si è resa necessaria la realizzazione di un validatore opportuno mediante la classe TelefonoValidator che implementa l’interfaccia Validator. 311 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 45 - JSF : TelefonoValidator Il criterio adottato per la validazione è il medesimo degli altri validatori ed è basato sulla definizione di un pattern a cui deve essere conforme un numero di telefono per essere considerato valido. ... Pattern mask = Pattern.compile("\\d+-{1}\\d+"); Matcher matcher = mask.matcher(value.toString()); // se il valore non corrisponde al pattern if (!matcher.matches()){ ... ... throw new ValidatorException(msg); } ... Allo stesso modo, il validatore è stato registrato nel file di configurazione ed utilizzato nelle pagine JSP mediante il tag <f:validator>. <h:inputText id="telefono" value="#{utente.telefono}" maxlength="15"> <f:validator validatorId="telefonoValidator" /> </h:inputText> 312 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Nel caso di esito negativo della validazione, il messaggio di errore prelevato dal Resource Bundle è del tipo in figura. Figura 46 - JSF : Esempio d'uso del TelefonoValidator 5.2 Plug-in Validator Struts Per poter effettuare la validazione dei dati contenuti nei form, il framework Struts mette a disposizione un proprio meccanismo che è strettamente legato ai formbeam associati a questi ultimi. Nell’ambito della realizzazione di un formbean mediante l’implementazione di una classe che estende ActionForm oppure una delle sue derivate, è possibile effettuare l’overriding del metodo validate() all’interno del quale va immesso il codice per la validazione. Tale metodo viene invocato automaticamente nel framework subito dopo che il formbean sia stato popolato. Con questo meccanismo, similmente a quanto accade in JSF, se esistono dei form distinti aventi dei campi che prevedono le medesime validazioni, è necessario riscrivere il metodo validate() per tutti i formbean associati. Per evitare problemi di manutenzione del software, il framework ingloba il plug-in Validator all’interno della propria distribuzione, il quale rappresenta uno strumento notevolmente potente. La prima considerazione da fare è che il plug-in va registrato come tale all’interno del file di configurazione dell’applicazione, specificando il percorso del file delle regole di validazione (validator-rules.xml) e del file in cui sono dichiarati i form da validare (validation.xml). <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/> </plug-in> Per quanto riguarda le regole di validazione, queste ultime non sono state assolutamente modificate e non ne sono state create delle ulteriori, in quanto il plugin mette a disposizione le funzionalità di validazione più comuni, tra cui anche il 313 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ controllo di correttezza di un indirizzo email. Le principali validation-rules utilizzate sono le seguenti : - required : controlla che un campo non sia vuoto, in quanto è obbligatoria l’immissione di un valore; - email : verifica che un valore abbia il formato corretto di un indirizzo email; - minlength : valuta se il valore di un campo rispetti una lunghezza minima prefissata; - mask : controlla che un certo valore sia conforme ad un pattern specificato; - date : verifica che un campo contenga una data corretta, secondo un certo formato; - float : valuta che un certo valore sia del tipo float; La disponibilità di queste regole non ha reso necessaria l’estensione del plug-in, realizzando delle funzioni che implementassero ulteriori regole di validazione. Da questo punto di vista, il framework Struts si può considerare superiore a JSF, tenendo conto che i validatori standard di quest’ultimo sono soltanto tre. Un’analisi più attenta è richiesta dal file validation.xml, all’interno del quale sono stati dichiarati tutti i form ed i relativi campi sui quali devono essere applicate delle regole di validazione specifiche. Tali form devono corrispondere esattamente ai formbean registrati nel file di configurazione, così come le proprietà di questi ultimi devono coincidere con i campi soggetti a validazione. Ad esempio, considerando il form per la registrazione di un utente, esso è dichiarato all’interno del file con una sintassi di questo tipo. <form name="registrazioneForm"> <field depends="required" property="userName"/> <field depends="required,minlength" property="password"> <var> <var-name>minlength</var-name> <var-value>5</var-value> </var> <arg0 key="${var:minlength}" name="minlength" resource="false"/> </field> <field depends="required,email" property="email"/> </form> 314 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Il nome specificato per il form deve coincidere con quello del formbean ad esso associato. Per ciascun campo ne viene specificato il nome, coincidente con la proprietà corrispondente nel formbean suddetto, e la regola di validazione da applicare. Sulla base di quanto detto, si osserva che i campi previsti dal form sono tutti obbligatori, regola required, ed inoltre al campo relativo all’indirizzo email è stato applicata la regola email. Infine, per quanto riguarda la password è stata utilizzata anche la regola di lunghezza minima, minlength, per la quale è stato dichiarato un parametro di ingresso indicante il valore di quest’ultima; nel caso specifico, si prevede che la password non sia lunga meno di 5 caratteri alfanumerici. Una regola particolarmente utilizzata è quella che permette la validazione di un valore in relazione ad un certo pattern specificato. Tale regola, nota come mask, prevede un parametro di ingresso che rappresenta il pattern per il controllo della conformità. Il caso principale di utilizzo è stato quello relativo ai valori che dovevano essere caratterizzati esclusivamente da una sequenza di cifre (es. C.A.P, numero della carta di credito, etc..) oppure nel caso del numero di telefono. <form name="acquistoSchedaForm"> <field depends="required" property="nome"/> <field depends="required" property="cognome"/> ... ... <field depends="mask" property="cap"> <var> <var-name>mask</var-name> <var-value>[0-9]+</var-value> </var> <msg key="cifreError" name="mask"/> </field> ... <field depends="mask" property="telefono"> <var> <var-name>mask</var-name> <var-value>[0-9]+-{1}[0-9]+</var-value> </var> <msg key="numTelefonoError" name="mask"/> </field> ... </form> La funzione mask ha previsto di volta in volta in ingresso, un pattern differente in relazione al valore da validare. Infine, una regola di validazione particolarmente interessante è quella relativa alla validazione di una data. Nell’implementazione in JSF, non si è reso necessario un controllo di questo tipo, in quanto il Custom 315 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Component realizzato impedisce la selezione di una data non valida. Nel caso di Struts, si è deciso di utilizzare un semplice campo di testo per l’inserimento di una data, in modo da poter utilizzare questa potente regola di validazione disponibile nel plug-in. ... <field depends="date" property="dataNascita"> <var> <var-name>date</var-name> <var-value>dd/MM/yyyy</var-value> </var> </field> ... Si osserva che la funzione date prevede un parametro di ingresso che definisce secondo quale pattern la data debba essere considerata corretta. Infine, un ulteriore vantaggio dell’uso del plug-in Validator è dato dal fatto che quest’ultimo supporta anche la validazione lato client., in quanto le regole di validazione non prevedono soltanto una serie di classi e metodi che le implementano ma anche i corrispondenti codici in Javascript. Per poter usufruire di questa funzionalità è necessario utilizzare il tag <html:javascript> all’interno della pagina JSP di interesse, specificando il nome del form su cui effettuare la validazione. 6. Model Il Model può essere considerato il cuore dell’applicazione realizzata, in quanto è costituito da tutte quelle classi che definiscono la business-logic. Così come anticipato in precedenza, l’implementazione in JSF ha previsto lo sviluppo dei backing beans e dei relativi metodi che realizzano le funzionalità del sistema. Con Struts si è resa necessaria la realizzazione delle action, per cui le classi della business-logic risultano notevolmente più “leggere”. Ovviamente, la logica che è alla base delle funzionalità è praticamente la stessa, con la differenza che in JSF è completamente incapsulata nei metodi dei backing beans mentre in Struts è distribuita fra gli oggetti della business-logic e le action. Queste ultime prevedono tutto ciò che riguarda l’accesso agli elementi del framework, mentre i primi eseguono 316 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ le operazioni previste dall’applicazione. Infine, sono state implementate alcune classi comuni tra le due implementazioni e che hanno i seguenti ruoli : - una serie di eccezioni che possono essere sollevate da particolari elaborazioni; - una serie di classi che gestiscono le operazioni di accesso alla base di dati; 6.1 Classi Exception L’applicazione prevede alcune funzionalità nell’ambito delle quali si possono verificare delle condizioni di errore che vanno opportunamente gestite. Uno dei metodi più “eleganti” è l’utilizzo del meccanismo delle eccezioni messo a disposizione dal linguaggio Java. Per questo motivo, sono state realizzate le seguenti classi che estendono la classe base Exception : - PasswordErrata : eccezione che si verifica nel caso in cui un utilizzatore abbia tentato un accesso al sistema sbagliando la password; - UsernameErrata : eccezione sollevata nel momento in cui un utilizzatore tenta di effettuare il login con una username non registrata; - UsernameEsistente : eccezione che si verifica in fase di registrazione, quando l’utente ha specificato una username già presente nella base di dati; - RuoloBloccato : eccezione sollevata quando un utilizzatore tenta di eseguire il login con username e password corretti, ma il suo account risulta bloccato a causa di tentativi di accesso errati in precedenza; - RuoloLoggato : eccezione che viene sollevata nel momento in cui un utilizzatore tenta di accedere al sistema ma risulta già loggato; Queste classi sono perfettamente identiche tra le due implementazioni ed inoltre sono praticamente tutte una semplice estensione della classe Exception alla quale non aggiungono alcuna funzionalità. Lo scopo della loro realizzazione è soprattutto concettuale, per poter distinguere all’interno del codice le eccezioni di tipo diverso. Soltanto la classe PasswordErrata è leggermente diversa dalla altre, in quanto ha la proprietà idRuolo che permette di individuare l’utilizzatore che ha immesso lo 317 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ username corretto ma la password sbagliata, per cui bisogna tenere conto dei tentativi di accesso errati e bloccarlo nel caso in cui sia necessario. Figura 47 - JSF e Struts : Classi Exception 6.2 Classi di interfacciamento con il DataBase Nella realizzazione di un sistema software che debba funzionare in locale oppure in rete, ci sono tipicamente degli oggetti per i quali va garantita la persistenza attraverso l’utilizzo delle basi di dati. Per poter gestire le operazioni di lettura e scrittura nel database, sono previste due possibili soluzioni : - ogni oggetto gestisce la propria persistenza in maniera autonoma, prevedendo dei metodi che accedono alla base di dati; - ogni oggetto delegata la gestione delle propria persistenza ad un’altra classe che mette a disposizione una serie di metodi di accesso al database; Con lo scopo di disaccoppiare le funzionalità della business-logic dalle operazioni di accesso alla base di dati, generalmente si preferisce adottare la seconda soluzione. Ciò è stato fatto anche nella Web application realizzata, implementando il sottolivello Data Access del Model attraverso un’ulteriore insieme di classi, una per ciascuna corrispondente classe della business-logic. 318 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per l’accesso vero e proprio alla base di dati, sono state utilizzate le seguenti classi JDBC (Java DataBase Connectivity) : - Connection : rappresenta una connessione alla base di dati; - Statement : definisce una generica query da eseguire sul database; - ResultSet : rappresenta un gruppo di record, risultato di una query eseguita sulla base di dati; - DriverMananger : classe che stabilisce la connessione con la base di dati utilizzando i driver JDBC; - SQLException : definisce un’eccezione generica che è sollevata nel caso in cui si verifichi un errore di accesso al database; Ogni classe realizzata prevede una serie di proprietà private che rappresentano la connessione (conn), una generica query (stmt), un resultSet (rs) ed infine la stringa di interrogazione (sQuery). Per poter garantire l’utilizzo dell’applicazione con un qualsiasi tipo di DBMS (es. MySQL, MS SQL Server, MS Access, Oracle, …) e facilitarne il passaggio dall’uno all’altro, è stata realizzata la classe astratta InfoDBMS che non ha metodi ma eslusivamente le seguenti proprietà : - driver : il driver da utilizzare per la connessione al database; - connString : la stringa di connessione che specifica l’URL del database; - user : il nome dell’utente che accede al database con opportuni privilegi; - userPassword : la password dell’utente suddetto; Le classi che si interfacciano con il database utilizzano le proprietà della classe suddetta, per impostare i parametri di accesso con i quali stabilire la connessione alla base di dati. In questo modo, è possibile utilizzare di volta in volta un DBMS differente, semplicemente modificando i campi statici all’interno della classe InfoDBMS, senza alcun intervento all’interno delle altre classi. Un ulteriore vantaggio è dato dal fatto che la configurazione è completamente centralizzata in un’unica classe. 319 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 48 - JSF e Struts : InfoDBMS Per quanto riguarda l’implementazione dei metodi di accesso, la struttura tipica di ciascuno di essi è la seguente. ... try { Class.forName(InfoDBMS.driver); conn=DriverManager.getConnection(InfoDBMS.connString, InfoDBMS.user, InfoDBMS.userPassword); stmt=conn.createStatement(); ... creazione della query string ... rs=stmt.executeQuery(sQuery); ... gestione del resultset ... } catch (SQLException e) { ... gestione eccezioni ... } finally { try {if (rs !=null) rs.close();} catch (SQLException se) {} try {if (stmt !=null) stmt.close();} catch (SQLException se) {} try {if (conn !=null) conn.close();} catch (SQLException se) {} } ... Viene eseguita un’operazione di caricamento dei Driver JDBC e di apertura della connessione alla base di dati specificata, dopodichè si crea la query string contenente l’interrogazione, la si esegue e si manipola il gruppo di record risultanti. E’ prevista inoltre la gestione di un’eventuale eccezione sollevata e la chiusura definitiva degli oggetti utilizzati. Per quanto riguarda le informazioni di accesso alla Web application è stata realizzata la classe AccessoDB, la quale dispone dei metodi per inserire, aggiornare e leggere un record nella tabelle degli accessi. Il metodo inserisci() ed aggiorna() 320 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ prevedono in ingresso un oggetto della classe Accesso, oltre all’identificativo del ruolo a cui le informazioni si riferiscono e la tipologia del ruolo stesso, utente oppure amministratore. Essi non restituiscono alcun valore, in quanto il loro scopo è eseguire un’operazione di scrittura nella base di dati. Il metodo leggi() prevede soltanto gli ultimi due parametri suddetti, accede al database e restituisce l’oggetto Accesso corrispondente. Infine, ci sono due metodi privati che permettono la conversione e la formattazione degli oggetti Date, sottolineando però che le date memorizzate all’interno del database non sono di tipo stringa. Figura 49 - JSF e Struts : AccessoDB Le informazioni che riguardano l’amministratore sono gestite attraverso la classe AmministratoreDB che mette a disposizione i metodi per eseguire l’operazione di login, per leggere ed aggiornare un record ed infine bloccare l’amministratore. 321 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 50 - JSF e Struts : AmministratoreDB In particolare, il metodo login() prevede in ingresso la username e la password dell’amministratore e ne verifica la validità, con la possibilità di sollevare una delle eccezioni introdotte in precedenza, se si verifica una particolare condizione di errore. Ovviamente, questo metodo è invocato dalla funzionalità della business-logic che permette di effettuare il login ed il suo scopo è solo ed esclusivamente di accesso alla base di dati. I metodi leggi() e blocca() prevedono in ingresso l’identificativo dell’amministratore di cui leggere le informazioni oppure da bloccare, mentre aggiorna() riceve un oggetto Amministratore. In maniera del tutto analoga è definita la classe UtenteDB che permette di garantire la persistenza delle informazioni dell’utente. Esso mette a disposizione i metodi tipici per la lettura, l’aggiornamento, l’inserimento e l’eliminazione di un record. I metodi leggi() ed elimina() prevedono in ingresso l’identificativo dell’utente di cui leggere le informazioni oppure da eliminare, mentre i metodi aggiorna() ed inserisci() prevedono un oggetto Utente. E’ da osservare che l’operazione di inserimento è piuttosto generica, in quanto essa viene utilizzata nella funzionalità di registrazione della business-logic, certamente più complessa. Il metodo login() opera allo stesso modo come per la classe AmministratoreDB. Inoltre sono previsti i seguenti ulteriori metodi : - blocca() : permette di bloccare o di sbloccare un utente; 322 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - elencoUtenti() : produce la lista degli utenti registrati; - esisteUsername() : verifica l’esistenza di uno username in fase di registrazione. - esisteEmail() : verificano l’esistenza di un indirizzo email. Viene utilizzato nella funzionalità di richiesta via mail delle informazioni di accesso da parte dell’utente e restituisce l’identificativo di quest’ultimo in caso di esito positivo; Figura 51 - JSF e Struts : UtenteDB La gestione della permanenza di tutto ciò che è strettamente legato ai brani MP3 viene eseguita mediante le tre seguenti classi : - BraniMP3DB; - PlaylistDB; - ComposizionePlaylistDB; 323 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Attraverso la classe BranoMP3DB è possibile gestire le informazioni di ciascun brano all’interno dell’archivio. Oltre ai tipici metodi di inserimento, eliminazione, lettura ed aggiornamento è previsto il metodo ricerca() che permette di caricare dal database una lista di brani. Esso viene utilizzato sia nel caso in cui si richieda di visualizzare i brani sulla base di un genere specificato, sia nel caso in cui viene effettuata un’operazione di ricerca in base a dei criteri specificati. I parametri di ingresso previsti sono relativi al genere, il titolo, l’autore e l’album dei brani da cercare. Figura 52 - JSF e Struts ; BranoMP3DB Per quanto riguarda la gestione della permanenza delle informazioni delle playlist, è stata realizzata la classe PlaylistDB che prevede le operazioni di inserimento, eliminazione e lettura oltre al metodo elencoPlaylist() che genera l’elenco delle playlist archiviate. In particolare, il metodo inserisci() prevede come parametri di ingresso un oggetto Playlist e l’identificativo dell’utente a cui esso è associato. 324 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 53 - JSF e Struts : PlaylistDB L’ultima classe, ComposizionePlaylistDB, non ha una classe corrispondente nella business-logic e mette a disposizione i metodi per poter inserire ed eliminare un brano MP3 da una playlist specificata oltre al metodo per generare l’elenco dei brani appartenenti ad essa. Il metodo inserisci() ha in ingresso l’identificativo della playlist e l’identificativo del brano da introdurre, allo stesso modo opera il metodo elimina(), mentre il metodo composizionePlaylist() riceve in ingresso l’identificativo di una playlist e restituisce l’elenco dei brani MP3 che sono al suo interno. 325 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 54 - JSF e Struts : ComposizionePlaylistDB Per quanto concerne le informazioni relative alla scheda prepagata, alle ricariche ed all’acquisto dei brani MP3, sono state realizzate le seguenti classi : - SchedaPrepagataDB; - RicaricaDB; - DownloadDB; La classe SchedaPrepagataDB fornisce i metodi base per l’inserimento, la lettura, l’aggiornamento e l’eliminazione di ciascun record che contiene le informazioni relative ad una generica scheda prepagata. In particolare, il metodo inserisci() prevede un oggetto SchedaPrepagata e l’identificativo dell’utente che ne ha effettuato l’acquisto. 326 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 55 - JSF e Struts : SchedaPrepagataDB La classe RicaricaDB permette di inserire all’interno della base di dati le informazioni relative ad una ricarica eseguita su una scheda prepagata. Non è prevista un’operazione di cancellazione, in quanto nella definizione della tabella corrispondente nel database è stato specificato un vincolo di “cascade” sull’evento “onDelete” sulla chiave esterna verso la tabella della scheda prepagata. In questo modo, effettuando una cancellazione di una scheda prepagata, vengono cancellati direttamente dal DBMS tutti i record delle corrispondenti ricariche. In questo modo si garantisce la coerenza delle informazioni nella base di dati, senza dare l’onere di questa operazione alle classi del software. 327 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 56 - JSF e Struts : RicaricaDB Infine, la classe DownloadDB mette a disposizione un unico metodo che permette di inserire un nuovo acquisto di un brano MP3 e quindi il relativo download. Non prevede alcun metodo di eliminazione in quanto la tabella corrispondente ha due chiavi esterne verso le tabelle relative ai brani ed agli utenti con un vincolo di “cascade” sull’evento “onDelete”. In questo modo, eliminando un brano MP3 oppure un utente, vengono cancellati tutti i record relativi ai corrispondenti download direttamente dal DBMS. Anche in questo caso, si garantisce la coerenza delle informazioni nel database, sollevando l’esecuzione di queste operazioni dalle classi del software. 328 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 57 - JSF e Struts : DownloadDB 6.3 Le Action di Struts Prima di analizzare le funzionalità offerte dal sistema e come esse sono state implementate all’interno della business-logic, per quanto riguarda il framework Struts si rende necessario un approfondimento sulle action che assumono un ruolo di collegamento (bridge) tra il Controller ed il Model vero e proprio. Bisogna tenere conto del fatto che all’interno di esse è prevista una parte del codice che interagisce con gli oggetti del framework ed utilizza i metodi delle classi della business-logic, che è in parte inevitabilmente presente in ciascuna action. Sono state suddivise in più package, a secondo che vengano utilizzate all’interno di uno specifico modulo dell’applicazione oppure siano comuni a più moduli. Di seguito è riportato l’elenco dei suddetti package : - package defaultactions - LoginAction : gestisce l’operazione di login al sistema; - RegistrazioneAction : esegue la registrazione di un utente; - RichiediPasswordAction : fornisce la funzionalità di richiesta dei dati di accesso via mail; 329 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - package adminactions - AmministratoreAction : esegue tutte le funzionalità relative all’amministratore; - - package useractions - PlaylistAction : offre le funzionalità di gestione delle playlist; - RicaricaSchedaAction : gestisce l’operazione di ricarica di una scheda; package commonactions - AcquistoSchedaAction : effettua l’operazione di acquisto di una scheda prepagata; - BranoMP3Action : fornisce tutte le funzionalità relative ai brani MP3; - UtenteAction : mette a disposizione le funzionalità che riguardano gli utenti; - LogoutAction : permette di eseguire l’operazione di logout; E’ da sottolineare che, per interazioni con gli oggetti del framework, si intende dire che all’interno di ciascuna action vengono eseguite le operazioni di accesso alle variabili di sessione e di ciascuna richiesta oltre al salvataggio dei messaggi di errore da visualizzare all’utente. Tali operazioni prevedono l’utilizzo delle classi di Struts o comunque legate alle Servlet in generale, per cui non sono eseguite all’interno degli oggetti della business-logic in modo da favorire il disaccoppiamento di quest’ultima dal framework stesso. Tali action sono state effettivamente implementate, ciascuna attraverso una corrispondente classe, ma ad esse va aggiunta la action switch appartenente alla classe SwitchAction che è stata soltanto dichiarata nei file di configurazione dei tre moduli ed il cui scopo è quello di permettere il passaggio fra un modulo e l’altro. Ciascuna delle action suddette è stata ovviamente dichiarata all’interno di uno o più moduli e ad essa sono stati associati i forward che permettono la navigazione all’interno della Web application. 330 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 58 - Struts : Action ed interazione con le classi del Model 331 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ La classe LoginAction deriva dalla classe base Action, per cui prevede l’unico metodo execute() all’interno del quale ci sono le elaborazioni effettuate dalla action stessa. Inoltre, prevede due metodi privati che permettono di gestire le condizioni di password errata e di login effettuato in maniera corretta. Figura 59 - Struts : LoginAction Il metodo execute() contiene tutte le operazioni necessarie per valutare la username e la password immessa dall’utilizzatore, invocando il metodo login() della classe Ruolo, permettendo l’accesso nel caso di esito positivo oppure catturando una delle eccezioni relative alle possibili condizioni di errore e salvandone i corrispondenti messaggi. Il metodo privato loginCorretto() ha il compito di salvare le informazioni 332 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ relative all’accesso mediante la classe Accesso e le sue derivate e di inserire in sessione le informazioni dell’utente oppure dell’amministratore che ha effettuato il login. Il metodo passwordErrata() segnala l’errore di password errata, ne tiene traccia mediante la classe Accesso e blocca eventualmente l’utilizzatore nel caso di due tentativi errati consecutivi. Infine, all’interno del metodo execute(), se il login va a buon fine per un utente, vengono caricate in sessione le informazioni di un’eventuale scheda prepagata associata a quest’ultimo ed eventualmente segnalata la sua scadenza. L’ultima operazione eseguita è l’utilizzo del forward verso la action switch per passare al modulo corretto, a seconda che l’accesso sia stato eseguito da un utente oppure dall’amministratore. La registrazione della action nel file di configurazione del modulo di default permette di associarle il formbean loginForm ed il forward in caso di errore. <action input="/pages/home.jsp" name="loginForm" path="/login" scope="request" type="defaultactions.LoginAction"> <forward name="failure" path="/pages/home.jsp"/> </action> La classe RegistrazioneAction estende anch’essa la classe Action ed all’interno del proprio metodo execute() gestisce le operazioni che riguardano la registrazione dell’utente, intercettando l’eventuale errore nel caso in cui la username scelta sia già presente in archivio oppure completando l’operazione in maniera corretta. Essa permette il passaggio alla action per la gestione dell’acquisto della scheda prepagata, se l’utente sceglie di voler usufruire di tale servizio. Infine, una volta completata la registrazione in maniera corretta viene invocato un forward per richiamare la action LoginAction ed eseguire il login automatico al sistema. Attraverso la dichiarazione del file di configurazione del modulo “default” viene assegnato il formbean registrazioneForm ed i forward per la navigazione. <action input="/pages/registrazione.jsp" name="registrazioneForm" path="/registrazione" scope="session" type="defaultactions.RegistrazioneAction" validate="true"> <forward name="login" path="/login.do"/> <forward name="acquistoScheda" path="/pages/acquistoScheda.jsp"/> <forward name="failure" path="/pages/registrazione.jsp"/> </action> 333 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 60 - Struts : RegistrazioneAction La classe RichiediPasswordAction estende Action ed all’interno del metodo execute(), utilizza il metodo richiediUsernamePassword() della classe Utente, facente parte della business-logic, per inviare la mail con i dati di accesso. Ovviamente, nel caso in cui l’indirizzo email specificato non risulti registrato all’interno dell’archivio, viene salvato un messaggio di errore. Figura 61 - Struts : RichiediPasswordAction 334 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ La registrazione viene eseguita soltanto del file struts-config.xml associando alla action il formbean richiediPasswordForm. <action input="/pages/richiestaPassword.jsp" name="richiediPasswordForm" path="/richiediPassword" scope="request" type="defaultactions.RichiediPasswordAction" validate="true"> <forward name="eseguita" path="/pages/richiestaPassword.jsp"/> </action> La classe AmministratoreAction deriva da DispatchAction, in quanto non ha il compito di effettuare un unico tipo di elaborazione ma di svolgere tutte le principali funzionalità legate all’amministratore. Ovviamente, una cosa di questo tipo non può essere gestita attraverso la derivazione dalla classe Action che contiene un unico metodo execute() o comunque sarebbe necessario definire all’interno di quest’ultimo una serie di costrutti condizionali per poter distinguere che tipo di operazioni effettuare. La soluzione più “elegante” per ovviare a questo problema è l’uso della classe DispatchAction che non prevede un unico metodo execute() ma più metodi, ciascuno dei quali realizza una specifica funzionalità. Quando tale action viene invocata, attraverso una particolare parametro dispatch è possibile distinguere di volta in volta quale dei suoi metodi eseguire. In particolare, la classe AmministratoreAction prevede i due seguenti metodi : - visualizza() : esegue le operazioni necessarie alla visualizzazione delle informazioni dell’amministratore; - modifica() : effettua le elaborazioni relative alla modifica della password richiesta dall’amministratore, ovviamente segnalando eventuali condizioni di errore; 335 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 62 - Struts : AmministratoreAction Essa è registrata esclusivamente nel modulo “admin” associandole il formbean modificaPasswordForm ed i forward in caso di visualizzazione/modifica oppure di insuccesso dovuto ad errori nell’aggiornamento. E’ da osservare la dichirazione del parametro dispatch attraverso l’attributo parameter. <action input="/pages/modificaDatiAmministratore.jsp" name="modificaPasswordForm" parameter="dispatch" path="/amministratore" scope="request" type="adminactions.AmministratoreAction" validate="false"> <forward name="visualizzaDati" path="/pages/modificaDatiAmministratore.jsp"/> <forward name="failure" path="/pages/modificaDatiAmministratore.jsp"/> </action> Anche la classe PlaylistAction estende DispatchAction, in quanto mette a disposizione tutte le funzionalità relative alla gestione delle playlist. 336 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Essa è costituita dai seguenti metodi, invocati sulla base di un opportuno parametro dispatch : - visualizzaElencoPlaylist() : permette di caricare l’elenco delle playlist relative all’utente in sessione oppure di segnalarne l’assenza; - eliminaPlaylists() : gestisce l’eliminazione di più playlist selezionate dall’elenco, ritornando poi alla visualizzazione dell’elenco aggiornato; - crea() : esegue le operazioni relative alla creazione della playlist per l’utente in sessione; - elimina() : gestisce l’eliminazione della playlist corrente; - visualizzaElencoBraniMP3Playlist() : permette la visualizzazione dell’elenco dei brani MP3 contenuti nella playlist corrente oppure di segnalarne l’assenza; - ascolta() : gestisce la creazione sul file system del server di un file M3U contente i percorsi ai file MP3 da eseguire ed avvia il player MP3 per l’ascolto della playlist; Figura 63 - Struts : PlaylistAction 337 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Essa è registrata esclusivamente nel modulo “user” associandole il formbean playlistForm per la creazione e cancellazione delle playlist ed i forward per le richieste di visualizzazione. <action name="playlistForm" parameter="dispatch" path="/playlist" scope="request" type="useractions.PlaylistAction" validate="false"> <forward name="visualizzaElencoPlaylist" path="/pages/gestionePlaylist.jsp"/> <forward name="failure" path="/pages/gestionePlaylist.jsp"/> <forward name="visualizzaElencoBraniPlaylist" path="/pages/visualizzaElencoBraniPlaylist.jsp"/> </action> La classe RicaricaSchedaAction deriva da Action e di conseguenza prevede soltanto il metodo execute(), in quanto il suo unico scopo è quello di gestire le operazioni per la ricarica di una scheda prepagata. Essa ovviamente ha dei legami di dipendenza con la classe Ricarica, CartaCredito e SchedaPrepagata della business-logic. La prima e la seconda per poterle istanziare rispettivamente con l’importo scelto dall’utente e le informazioni della carta di credito e l’ultima per poter eseguire l’operazione di ricarica vera e propria della scheda prepagata, la quale opera sugli oggetti suddetti. Figura 64 - Struts : RicaricaSchedaAction Anche in questo caso la dichiarazione della action compare solo nel modulo “user” con il formbean ricaricaSchedaForm associato. 338 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ <action input="/pages/ricaricaScheda.jsp" name="ricaricaSchedaForm" path="/ricaricaScheda" scope="request" type="useractions.RicaricaSchedaAction" validate="true"> <forward name="success" path="/home.jsp"/> </action> La classe AcquistoSchedaAction deriva da Action e mette a disposizione la funzionalità di acquisto di una scheda prepagata sia nella fase di registrazione per un nuovo utente sia per un utente già registrato ma che vuole usufruire del servizio di download dei brani MP3. All’interno del metodo execute(), si valuta in quale delle due possibili situazioni la action è stata invocata, per determinare se si rende necessaria una preventiva operazione di registrazione dell’utente oppure direttamente l’acquisto della scheda prepagata. Negli oggetti del tipo SchedaPrepagata e CartaCredito vengono copiate le informazioni relative agli importi ed alla carta di credito, prelevate dal formbean, per poi eseguire l’acquisto. In caso di registrazione, il metodo termina invocando la action per l’esecuzione del login in automatico. La action è stata registrata sia nel modulo “default” che “user” considerando le due possibili situazioni in cui è previsto l’acquisto della scheda prepagata. Figura 65 - Struts : AcquistoSchedaAction 339 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ La classe BranoMP3Action è una delle più complesse in quanto deriva da DispatchAction e mette a disposizione una serie di metodi che permettono di gestire tutte le possibili funzionalità sui brani MP3. Tali metodi sono i seguenti : - visualizzaElencoBraniMP3() : permette la visualizzazione dei brani MP3 sulla base del genere specificato, inserendo la lista ricavata dal database all’interno di una variabile di sessione; - ricercaBraniMP3() : esegue la ricerca dei brani sulla base dei criteri di ricerca fissati ed opera allo stesso modo del metodo precedente; - eliminaBraniMP3() : gestisce l’eliminazione di più brani contemporaneamente, selezionati dall’elenco corrente. Oltre alla cancellazione delle informazioni dall’archivio, vengono cancellati anche i file MP3 corrispondenti dal file system del server; - inserisci() : permette l’inserimento di un brano in archivio nonché il caricamento del corrispondente file MP3 sul server e la gestione della coerenza dei tag ID3v1 con le informazioni inserite; - visualizza() : esegue il caricamento delle informazioni di uno specifico brano in una variabile di sessione, garantendo una visualizzazione diversa a seconda che la richiesta sia stata fatta dall’utente oppure dall’amministratore; - elimina() : permette di effettuare l’eliminazione del brano corrente dall’archivio, nonché la cancellazione del file MP3 associato; - modifica() : permette all’amministratore di modificare le informazioni di un brano, aggiornando anche i tag ID3v1 del file MP3 corrispondente; - inserisciBraniMP3Playlist() : effettua l’inserimento di una serie di brani selezionati dall’elenco corrente all’interno di una playlist scelta e salvata in sessione; - eliminaBraniMP3Playlis() : permette l’eliminazione di più brani da una playlist, selezionandoli dall’elenco di composizione; - inserisciPlaylist() : permette di inserire il brano corrente all’interno di una playlist selezionata; - acquista() : effettua l’operazione di acquisto di un brano MP3 e ne abilita la funzionalità di download; 340 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Essa è praticamente legata a tutte le classi che riguardano i brani, il loro acquisto e la composizione delle playlist. Infine, essa è dichiarata sia nel modulo corrispondente all’utente che all’amministratore ovviamente garantendo l’esecuzione di metodi diversi. Figura 66 - Struts : BranoMP3Action La classe UtenteAction estende DispathAction e fornisce tutte le funzionalità strettamente legate all’utente. Essa prevede i seguenti metodi : - visualizza() : permette il caricamento dei dati dell’utente in sessione per permetterne la modifica oppure esclusivamente la visione all’amministratore; 341 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - modifica() : effettua l’operazione di aggiornamento dei dai dell’utente, segnalando eventuali condizioni di errore; - elimina() : permette all’amministratore di eliminare l’utente selezionato; - visualizzaUtenti() : esegue il caricamento dell’elenco degli utenti registrati in una variabile di sessione, per la successiva visualizzazione; - eliminaUtenti() : permette all’amministratore di eliminare uno o più utenti selezionati dall’elenco; - sblocca() : esegue l’operazione di sblocco di un utente; Si osservi che essa è strettamente legata solo ed esclusivamente alla classe Utente della business-logic, in quanto utilizza i metodi di quest’ultima che agiscono sull’utente. Inoltre, anche in questo caso la registrazione è stata effettuata nei moduli “admin” e “user”. Figura 67 - Struts : UtenteAction 342 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ L’ultima classe, LogoutAction, deriva da Action in quanto attraverso l’unico metodo execute() permette all’utente oppure all’amministratore di eseguire il logout dal sistema. Figura 68 - Struts : LogoutAction Invoca il metodo di logout() della classe Utente oppure Amministratore ed utilizza la classe Playlist per eliminare eventuali file MP3 di playlist ascoltate dall’utente. La dichiarazione nel modulo “user” e “admin” associa ad essa il forward verso la action switch per ritornare al modulo “default”. Considerando l’operazione di passaggio da un modulo all’altro durante la navigazione, è stata realizzata la action switch del tipo SwitchAction, la quale viene invocata con una sintassi di questo tipo : /switch.do?page=[pagina.jsp]&prefix=[prefissoModulo] in cui il parametro prefix permette di specificare il modulo verso il quale eseguire il passaggio (/admin, /user oppure una stringa vuota per il modulo di default) mentre page indica la pagina verso la quale essere redirezionati all’interno del modulo stesso. 343 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 6.4 Business-Logic e funzionalità del sistema Considerando le due implementazioni con JSF e Struts, le classi che costituiscono la business-logic sono caratterizzate dalle seguenti differenze : - con JSF costituiscono a tutti gli effetti i backing beans, per cui la maggior parte dei metodi restituiscono gli outcome per la navigazione essendo invocati direttamente dalle pagine JSP; con Struts vengono utilizzate all’interno delle action per cui non devono assolutamente occuparsi della navigazione; - con JSF i metodi accedono agli oggetti del framework, così come alle variabili di sessione e di ciascuna richiesta; con Struts tali operazioni vengono eseguite dalle action in modo che gli oggetti della business-logic siano disaccoppiati dal framework stesso; Facendo riferimento alle elaborazioni eseguite per ciascuna funzionalità, basta osservare che nel caso di JSF sono concentrate nei metodi delle classi stesse, mentre con Struts sono distribuite tra questi ultimi e le action. Inoltre, è da sottolineare che dal punto di vista concettuale sono sostanzialmente identiche, tenendo comunque conto che la Web application è la medesima. Una precisazione è doverosa per quanto riguarda l’inserimento e la lettura degli oggetti del sistema all’interno delle variabili di sessione. Queste ultime sono ampiamente utilizzate per memorizzare alcuni oggetti della business-logic che contengono le informazioni associate all’utilizzatore corrente, di cui bisogna mantenerne lo stato durante la navigazione. Per poter gestire le operazioni di lettura e scrittura in sessione, le classi sono state dotate di alcuni metodi statici che ovviamente necessitano dell’accesso all’istanza della classe HttpSession. Questo tipo di operazione non comporta nulla sull’implementazione con JSF, mentre sembrerebbe introdurre una contraddizione nell’implementazione con Struts, perché in questo modo si introduce un legame con il framework. La scelta è stata fatta per rendere più “snello” il codice all’interno delle action ma ciò non toglie che, a meno dei metodi statici suddetti, le classi della business-logic dell’implementazione in Struts sono assolutamente indipendenti dal framework e riutilizzabili con framework diversi. 344 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Tali metodi statici, previsti esclusivamente per alcune classi, hanno una nomenclatura di questo tipo : - getClasseSession : permette di recuperare dalla sessione un oggetto appartenente a questa classe; - removeClasseSession : elimina dalla sessione l’oggetto della classe; - putClasseSession : inserisce all’interno della sessione un oggetto di questa classe; - isClasseSession : verifica se un oggetto della classe è presente all’interno della sessione corrente; Pagina JSP Pagina JSP Oggetto BL (Backing Bean) Action Elaborazioni Oggetto BL Elaborazioni Figura 69 - JSF e Struts : Accesso agli oggetti della Business-Logic Di seguito sono descritte le classi che compongono il Model in entrambe le implementazioni, evidenziando l’interazione tra di esse e le differenze che sussistono tra le loro realizzazioni in un’implementazione e nell’altra. 345 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 6.4.1 Package accesso Questo package contiene le classi che permettono di gestire le informazioni che riguardano tutte le operazioni di login e di logout eseguite dagli utenti e dall’amministratore. Esso è caratterizzato dalla classe astratta Accesso dalla quale derivano le classi AccessoUtente ed AccessoAmministratore per distingure, unicamente da un punto di vista concettuale, le informazioni di accesso dell’utente e dell’amministratore. In entrambe le implementazioni, le proprietà delle classi sono le seguenti : - id : identificativo dell’accesso; - dataOraLogin, dataOraLogout : data ed ora in cui è stato eseguito il login ed il logout; - loginValido : valore booleano che indica se l’accesso è andato a buon fine o meno; - tentativo : intero che indica in corrispondenza di quale tentativo c’è stato l’accesso oppure eventualmente il blocco; - indirizzoIP : indirizzo IP del client; Ovviamente, nella classe Accesso i metodi sono tutti astratti ma sono implementati all’interno delle classi derivate e sono i seguenti : - inserisci() : permette di inserire le informazioni relative ad un’azione di login. Prevede in ingresso l’identificativo dell’utilizzatore che ha effettuato l’accesso; - leggi() : permette di leggere le informazioni dell’accesso di un utilizzatore, specificando in ingresso il suo identificativo; - aggiorna() : permette di aggiornare le informazioni dell’accesso in fase di logout, ricevendo in ingresso l’identificativo dell’utilizzatore che sta abbandonando il sistema; Nelle due implementazioni, queste classi sono perfettamente uguali considerando che non sono utilizzate direttamente dalle pagine JSP oppure dalle action, ma da altre classi della business-logic ed in particolar modo dagli oggetti Ruolo, Utente ed 346 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Amministratore. La differenza sostanziale è relativa ai metodi che accedono alla sessione, in quanto con JSF si utilizza la classe del framework FacesContext, mentre con Struts direttamente l’oggetto HttpSession. Figura 70 – JSF : package accesso 347 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 71 - Struts : package accesso Questo package non rappresenta un termine di paragone tra le due implementazioni e potrebbe essere banalmente interscambiato facendo gli opportuni accorgimenti nell’accesso alle variabili di sessione. 6.4.2 Package ruolo Il package ruolo contiene le classi relative agli utilizzatori della Web application. In particolare, la classe Ruolo generalizza le due sottoclassi Utente ed Amministratore, tenendo conto del fatto che prima di essere registrato o comunque di 348 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ eseguire il login, un utilizzatore dell’applicazione non può essere classificato. In entrambe le implementazioni, essa prevede le seguenti proprietà : - id : identificativo univoco del ruolo; - userName : username del ruolo; - password : password di accesso del ruolo; - bloccato : indica se il ruolo risulta bloccato o meno; - loggedIn : indica se il ruolo è loggato o meno al sistema; - tipo : è presente soltanto nell’implementazione in Struts per poter distinguere un utente e l’amministratore in fase di login, considerando che tale operazione viene gestita da una action esterna; Ovviamente, esse sono ereditate dalle classi Utente ed Amministratore essendo informazioni comuni ad entrambi, con la differenza che mentre la seconda non aggiunge ulteriori proprietà, la prima ha delle proprietà in più che rappresentano tutti i dati anagrafici dell’utente. Inoltre, sono previste le proprietà dataOraRegistrazione ed acquistoMP3 che indica se l’utente ha deciso di usufruire del servizio di acquisto e download dei brani. Nell’implementazione in JSF, la classe Utente necessita di una proprietà in più, elimina, che tiene conto del fatto che l’utente sia stato selezionato o meno all’interno dell’elenco degli utenti registrati per poter essere cancellato. Tale proprietà non si è resa necessaria in Struts, grazie all’utilizzo delle multibox, come visto nella descrizione della View. Prima di descrivere in linea generale il comportamento dei metodi previsti nelle classi, è opportuno osservare le dipendenze che ci sono tra di esse e le altre classi del Model oltre ad eventuali classi del framework adottato, in modo da poter comprendere alcune differenze tra le due implementazioni. Nell’implementazione con JSF, le classi utilizzano fortemente FacesContext per poter accedere alle variabili di sessione, mentre con Struts soltanto le classi Utente ed Amministratore accedono alle istanze di HttpServletRequest ed HttpSession. Queste ultime sono state utilizzate per far gestire a tali classi la propria permanenza in sessione ma, come anticipato, non sono strettamente necessarie e potrebbero essere usate dalle action esterne. La classe Ruolo non è assolutamente legata a queste classi, in quanto il suo uso principale avviene nella action di login , LoginAction, che gestisce la 349 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ persistenza in sessione delle informazioni di un utente oppure dell’amministratore, utilizzando i metodi delle classi corrispondenti. In entrambe le implementazioni, la classe Ruolo utilizza le classi UtenteDB ed AmministratoreDB per poter effettuare l’operazione di login, così come in entrambi i casi è legata alle eccezioni che possono essere sollevate in fase di accesso al sistema. Con JSF, Ruolo utilizza la classe Accesso e le sue derivate per poter gestire il salvataggio delle informazioni di accesso, mentre con Struts questo compito è delegato alle action. Infine, nell’implementazione in JSF, la classe Utente accede a tutte quelle classi legate a funzionalità accessibili dall’utente, tra cui Playlist, SchedaPrepagata, CartaCredito e Mail, mentre con Struts alcune di queste dipendenze mancano essendo previste nelle action. E’ da sottolineare che sussistono molte più dipendenze con JSF che non con Struts e tutto ciò è giustificato dal fatto che nel primo caso, le classi devono gestire tutto in maniera autonoma interagendo tra loro e con gli oggetti del framework, mentre nel secondo caso, le action si occupano di alcune di queste interazioni, per cui le classi della business-logic non fanno altro che eseguire semplici operazioni utilizzando le classi di accesso alla base di dati. 350 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 72 - JSF : package ruolo 351 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 73 - Struts : package ruolo 352 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per quanto riguarda i metodi, la classe Ruolo prevede : - login(), logout() : per effettuare le operazioni di login e di logout al sistema; - blocca(), sblocca() : per bloccare o sbloccare un ruolo. Essi non hanno corpo in entrambe le implementazioni ma vengono utilizzate le versioni soggette ad overriding delle classi Utente ed Amministratore; Nel caso dell’implementazione con JSF, è da osservare che sono presenti alcuni metodi privati che invece, nell’implementazione con Struts, si trovano nella LoginAction. Il metodo login() opera secondo una modalità di questo tipo : - tenta di valutare se al sistema sta accedendo un utente invocando UtenteDB.login(); - se lo username non è registrato tra gli utenti, tenta di valutare se sta accedendo l’amministratore invocando AmministratoreDB.login(); - nel caso di username inesistente in entrambi i casi viene segnalato un errore, mentre in caso di esito positivo e di password corretta viene consentito l’accesso. Se si tratta di un utente, vengono caricate le informazioni di un’eventuale scheda prepagata e ne viene valutata la scadenza; - in tutti gli altri casi, le classi di accesso alla base di dati possono sollevare le eccezioni relative a password errata, ruolo loggato e ruolo bloccato; La differenze principali tra le due implementazioni sono le seguenti : - con JSF, il parametro di ritorno del metodo sarà comunque una stringa che rappresenta l’outcome con il quale proseguire la navigazione. Con Struts, viene restituito un oggetto Ruolo che può puntare ad un’istanza di Utente oppure Amministratore. Tale oggetto verrà utilizzato dalla LoginAction che invoca questo metodo; - con JSF, le eccezioni sollevate nelle possibili condizioni di errore vengono catturate e poi ne viene gestita la segnalazione. Con Struts, considerando che il metodo login() viene invocato dalla LoginAction, le eccezioni vengono 353 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ catturate e poi risollevate a quest’ultima che avrà il compito di salvare i messaggi di errore nella request; - con JSF, la memorizzazione delle informazioni di accesso viene eseguita all’interno della classe Ruolo attraverso dei metodi privati. Con Struts, gli stessi metodi sono previsti nella LoginAction; - con JSF, nel caso di accesso di un utente, le informazioni di un’eventuale scheda prepagata vengono caricate all’interno del metodo login() stesso. Con Struts, tale operazione è delegata alla LoginAction. Il fatto che alcune elaborazioni siano eseguite nella LoginAction, per quanto riguarda l’uso di Struts, evidenzia che gli oggetti della business-logic eseguono soltanto le operazioni che competono loro da un punto di vista concettuale. Il metodo logout() è completamente diverso, in quanto con JSF, esegue semplicemente l’invalidazione della sessione mentre altre operazioni specifiche vengono eseguite dalla versione soggetta ad overriding delle classi Utente ed Amministratore. Con Struts, tale metodo non ha corpo in quanto vengono utilizzate esclusivamente le sue versioni sovrascritte che sono invocate all’interno della LogoutAction che si occupa anche di invalidare la sessione; La classe Amministratore prevede i seguenti metodi : - login(), logout() : versioni sovrascritte delle medesime funzioni di Ruolo. La login() non fa altro che invocare la versione della superclasse mentre la logout(), in entrambe le implementazioni, aggiorna le informazioni di uscita dal sistema; - visualizzaDati() : ha il compito di permettere la visualizzazione dei dati dell’amministratore per un’eventuale modifica. Con JSF, ricava le informazioni dall’oggetto Amministratore in sessione e restituisce l’outcome per la navigazione alla pagina di visualizzazione. Con Struts, esegue la lettura dei dati dal database con AmministratoreDB.leggi(), restituendo l’oggetto Amministratore; - modificaDati() : esegue la modifica dei dati dell’amministratore. Con JSF, esegue l’aggiornamento invocando AmministratoreDB.aggiorna() oppure segnala eventuali errori e fa proseguire la navigazione restituendo l’outcome. Con 354 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Struts, effettua l’aggiornamento allo stesso modo ma viene invocato dal metodo modifica() di AmministratoreAction, che ha il compito di gestire gli errori e passare alla pagina di visualizzazione; - blocca() : è perfettamente uguale in entrambe le implementazioni, bloccando l’amministratore mediante il metodo AmministratoreDB.blocca(); La classe Utente è sicuramente una delle più complesse, in quanto prevede i seguenti metodi : - login(), logout() : versioni sovrascritte delle medesime funzioni di Ruolo. La login() non fa altro che invocare la versione della superclasse mentre la logout(), in entrambe le implementazioni, aggiorna le informazioni di uscita dal sistema; - registrazione() : permette di effettuare la registrazione dell’utente; - visualizzaDati() : ha il compito di permettere la visualizzazione dei dati dell’utente. Con JSF, ricava le informazioni dall’oggetto Utente in sessione e restituisce un outcome diverso per la navigazione, in base al fatto che la richiesta sia stata fatta dall’utente, che vorrà modificare i propri dati, oppure dall’amministratore che potrà soltanto visionarli. Con Struts, esegue la lettura dei dati dal database con UtenteDB.leggi(), restituendo l’oggetto Utente; - modificaDati() : esegue la modifica dei dati dell’utente. Con JSF, esegue l’aggiornamento invocando UtenteDB.aggiorna() oppure segnala eventuali errori e fa proseguire la navigazione restituendo l’outcome. Con Struts, effettua l’aggiornamento allo stesso modo ma viene invocato dal metodo modifica() di UtenteAction, che ha il compito di gestire gli errori e passare alla pagina di visualizzazione; - blocca(), sblocca() : sono uguali in entrambe le implementazioni ed eseguono le operazioni di blocco e sblocco di un utente, mediante i corrispondenti metodi della classi che accedono alla base di dati; - sbloccaUtente() : presente soltanto nell’implementazione in JSF, è utile per non alterare la struttura comune dei metodi precedenti e può essere invocato da una pagina JSP per sbloccare l’utente e restituire l’outcome per la navigazione; 355 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - visualizzaElencoUtenti() : permette la visualizzazione dell’elenco degli utenti registrati. Con JSF, invoca UtenteDB.elencoUtenti() e salva la lista in una variabile di sessione restituendo l’outcome per la navigazione. Con Strust, viene invocato dal metodo visualizzaUtenti() della UtenteAction al quale restituisce la lista degli utenti registrati. E’ la action che si occupa di salvare tale lista nella sessione; - elimina(), eliminaUtenti() : permettono di eliminare un singolo utente oppure più utenti selezionati dall’elenco. In entrambe le implementazioni viene utilizzato uno o più volte il metodo UtenteDB.elimina(). Con JSF, viene gestito l’aggiornamento dell’elenco in sessione e restituito l’outcome per la navigazione. Con Struts, tali operazioni vengono eseguite dai metodi elimina() ed eliminaUtenti() della UtenteAction che gestisce anche il proseguimento della navigazione; - verificaUsername() : permette di valutare se un certo username risulta essere già associato ad un utente registrato. Esso viene utilizzato in fase di registrazione e l’implementazione con JSF, una volta terminata la verifica, restituisce un outcome per la navigazione. Con Struts, restituisce semplicemente un valore booleano e viene utilizzato dalla RegistrazioneAction. In entrambi i casi, viene utilizzato il metodo UtenteDB.esisteUsername(); - richiediUsernamePassword() : permette all’utente di ricevere una mail contenente i dati di accesso al sistema. Con JSF, verifica l’esistenza della mail utilizzando UtenteDB.esisteEmail(), invia i dati mediante la classe Mail oppure segnala eventuali errori. Con Struts, invia la mail allo stesso modo e restituisce un semplice valore booleano alla RichiediPasswordAction da cui viene invocato; Un’osservazione da fare è che i metodi visualizzaElencoUtente() ed eliminaUtenti() sono dichiarati statici con Struts ma non con JSF. Il fatto di essere statici, ossia non legati ad una specifica istanza della classe, è plausibile tenendo conto del fatto che non eseguono operazioni legate ad uno specifico utente ma agli utenti in generale. Con JSF, non è possibile dichiararli statici in quanto la loro invocazione avviene direttamente dalle pagine JSP e l’oggetto Utente è istanziato e presente in sessione. Inoltre, l’utilizzo del method-binding prevede di poter invocare metodi di istanza, appartenenti ai backing beans dichiarati, ma non metodi statici. 356 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Il metodo registrazione() merita un’analisi a parte, in quanto ha un’implementazione completamente diversa fra Struts e JSF. Con Struts, non fa altro che invocare UtenteDB.inserisci() per salvare le informazioni dell’utente nell’archivio. Con JSF, ha una struttura notevolmente complessa, poichè esegue tutte le operazioni legate alla registrazione che non prevede soltanto il salvataggio dell’utente in archivio. E’ ovvio che tali operazioni sono eseguite anche nell’implementazione in Struts ma non all’interno di questo metodo, bensì nella RegistrazioneAction. La funzionalità di registrazione, in entrambi i casi, è caratterizzata dalle seguenti fasi : - si valuta se l’utente abbia scelto di acquistare una scheda prepagata; - se così non fosse, viene semplicemente salvato l’utente in archivio oppure vengono segnalati eventuali errori dei dati; - viceversa, se l’utente vuole usufruire del servizio di acquisto e download dei brani MP3, vengono ricavate le informazioni della carta di credito e verificata la copertura, per poi eseguire l’acquisto della scheda prepagata; - viene effettuato il login automatico al sistema; Nel caso dell’implementazione in Struts, tutte le operazioni che riguardano la lettura dei form con i dati, la verifica della carta di credito, l’acquisto della scheda prepagata, la segnalazione degli errori e la gestione delle variabili di sessione vengono eseguite nelle due action RegistrazioneAction ed AcquistoSchedaAction, delegando al metodo registrazione() il solo compito di salvare l’utente in archivio. 6.4.3 Package brani Il package brani contiene fondamentalmente le due classi relative alla gestione dei brani MP3 presenti in archivio : BranoMP3 e FileMP3. Per quanto riguarda le proprietà che le caratterizzano, esse sono completamente uguali tra le due implementazioni, in quanto la classe BranoMP3 contiene le informazioni relative ad un generico brano, mentre FileMP3 ha alcune proprietà che permettono di accedere al file MP3 associato ad un brano ed altre che rappresentano i tag ID3v1. La differenza sostanziale, sempre in relazione alle proprietà, è caratterizzata dal fatto che 357 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ l’implementazione in JSF ne prevede delle ulteriori per poter gestire gli elenchi dei brani. La proprietà elimina permette di valutare se un brano è stato selezionato per l’eliminazione dall’archivio attraverso una corrispondente checkbox, così come inserisciInPlaylist ed eliminaDaPlaylist permettono di verificare se un brano è stato selezionato per essere inserito oppure eliminato da una playlist. Tali proprietà non sono previste con Struts, in quanto gli elenchi dei brani prevedono l’uso delle multibox, come visto nella descrizione della View. Un’ulteriore differenza riguarda la proprietà mp3 della classe FileMP3. Tale proprietà ha il compito di contenere un riferimento al file MP3 residente in locale, che è stato selezionato dall’amministratore per l’upload sul server. Facendo riferimento a quanto detto nella descrizione della View, Struts mette a disposizione il tag per la selezione del file e quindi la classe corrispondente FormFile, mentre JSF deve affidarsi alle estensioni di MyFaces, ossia al progetto Tomahawk che prevede la classe UploadedFile. Per questo motivo, la proprietà mp3 è di tipo diverso considerando le due implementazioni, ma il suo significato ed il relativo uso sono i medesimi. Dai diagrammi UML è possibile evidenziare le dipendenze che ci sono tra queste due classi e le restanti classi del Model, con le quali interagiscono. Per entrambe le implementazioni, la classe BranoMP3 è legata alle classi Download, BranoMP3DB, SchedaPrepagata ed Utente, rispettivamente per : - tenere traccia del download di un brano; - gestire la persistenza delle informazioni del brano all’interno dell’archivio; - eseguire l’operazione di acquisto di un brano; - garantire il legame con l’utente, in virtù di un acquisto e del download; 358 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 74 - JSF : package brani 359 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 75 - Struts : package brani La classe FileMP3 utilizza le classi MP3File e ID3v1 della Java MP3 Tag Library per poter accedere ai tag ID3v1 del file MP3 associato ad un brano e garantirne la coerenza con le informazioni di quest’ultimo presenti in archivio. Inoltre, essa è legata alle classi File, InputStream ed OutputStream per stabilire un riferimento alla directory sul server in cui caricare il file MP3 e gestire la lettura del file in locale con la corrispondente scrittura in remoto, attraverso due stream di byte. Le differenze sostanziali tra le due implementazioni riguardano : - il legame tra FileMP3 e UploadedFile con JSF ma con FormFile con Struts, in virtù di quanto detto in precedenza; 360 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - la permanenza delle informazioni di un brano all’interno di una variabile di sessione. Così come per tutte le altre classi che la prevedono, JSF utilizza la classe FacesContext mentre con Struts si usano direttamente le classi HttpServletRequest ed HttpSession. E’ da ricordare e sottolineare che in quest’ultimo caso, la gestione delle variabili in sessione potrebbe essere eseguita dalle action che agiscono sui brani e non sono strettamente necessarie nelle classi della business-logic; Analizzando in linea generale i metodi della classe BranoMP3, essi svolgono le seguenti funzionalità : - visualizzaInfo() : permette la visualizzazione delle informazioni di un brano selezionato. Con JSF, il brano è automaticamente presente nella request e viene spostato in sessione, restituendo un outcome diverso per la navigazione in base al fatto che la richiesta sia stata fatta dall’amministratore, che potrà modificare le informazioni del brano, oppure dall’utente che potrà esclusivamente visionarle. Con Struts, tale metodo viene invocato dal metodo visualizza() della BranoMP3Action. Il suo compito è quello di ricavare le informazioni del brano dal database mediante BranoMP3DB.leggi() restituendo un oggetto BranoMP3 alla action che avrà il compito di salvarla in sessione e far proseguire la navigazione; - modificaInfo() : permette la modifica delle informazioni di un brano, garantendo la coerenza con i tag ID3v1 del file MP3 associato. L’implementazione di questo metodo è sostanzialmente diversa nei due casi, in quanto se con JSF tutte le elaborazioni necessarie sono concentrate nel metodo, con Struts alcune di esse sono eseguite dal metodo modifica() della BranoMP3Action. In particolare, con JSF viene individuato il percorso del file sul server utilizzando la classe FileMP3 e viene effettuato l’aggiornamento dei tag sulla base delle nuove informazioni immesse dall’amministratore. Ovviamente, viene invocato il metodo BranoMP3DB.aggiorna() per garantire la modifica all’interno dell’archivio e viene restituito l’outcome per la navigazione. Con Struts, il metodo si limita ad aggiornare le informazioni in archivio, 361 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ mentre è compito della action corrispondente effettuare le operazioni di coerenza con i tag ID3v1 e far proseguire la navigazione; - visualizzaElencoBraniMP3(), ricerca() : questi due metodi operano allo stesso modo, nel senso che permettono la visualizzazione di un elenco di brani sulla base di un genere selezionato oppure in virtù di criteri di ricerca più raffinati. Con JSF, utilizzano il metodo BranoMP3DB.ricerca() e caricano la lista ottenuta all’interno della sessione, restituendo poi l’outcome per la navigazione. Con Struts, essi vengono visualizzaElencoBraniMP3() invocati e rispettivamente ricercaBraniMP3() della dai metodi BranoMP3Action, restituendo un oggetto contenente la lista dei brani. E’ compito della action salvare tale elenco in sessione e far proseguire la navigazione; - acquista() : permette di effettuare l’acquisto di un brano. La sua implementazione è più o meno la stessa in entrambi i casi, in quanto vengono utilizzate le classi SchedaPrepagata e Download, rispettivamente per controllare l’importo residuo sulla scheda ed effettuare l’acquisto e poi abilitare il download. La differenza sta nel fatto che con JSF si accede alle informazioni della scheda direttamente dalla sessione, si segnalano eventuali errori e viene restituito l’outcome per la navigazione, mentre con Struts le informazioni della scheda sono passate con un parametro di ingresso e l’accesso alla sessione viene eseguito dal metodo acquista() della BranoMP3Action; - inserisci() : permette l’inserimento di un nuovo brano in archivio ed il caricamento del file MP3 corrispondente sul server. Opera allo stesso modo del metodo modifica() ed è implementato in maniera diversa nei due casi. Con JSF, viene individuato il path sul server in cui caricare il file e l’upload viene eseguito attraverso il metodo FileMP3.upload(), dopodichè viene valutata la coerenza tra i tag ID3v1 e le informazioni immesse dall’amministratore. Nel caso di esito positivo, viene invocato il metodo BranoMP3DB.inserisci() mentre in caso di esito negativo vengono segnalati gli errori. In entrambi i casi viene restituito l’outcome per proseguire la navigazione. Con Struts, il metodo esegue semplicemente l’inserimento delle informazioni del brano in archivio, mentre è il metodo inserisci() della BranoMP3Action che si occupa delle operazioni di upload del file, del controllo della coerenza dei tag ID3v1 e del proseguimento della navigazione; 362 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - elimina(), eliminaBraniMP3() : permettono di eseguire l’eliminazione di un singolo brano oppure di più brani selezionati da un elenco. Entrambi utilizzano uno o più volte il metodo BranoMP3DB.elimina(), per cancellare le informazioni del/i brano/i dall’archivio. Per quanto riguarda l’eliminazione del/i corrispondente/i file/s MP3 nel file system, con JSF questa operazione viene eseguita all’interno di questi metodi, invocando FileMP3.elimina(), mentre con Struts all’interno dei metodi corrispondenti elimina() ed eliminaBraniMP3() della BranoMP3Action. Un’osservazione interessante da fare, riguarda la differenza di signature che hanno alcuni metodi tra l’implementazione con JSF e con Struts. I metodi modificaInfo() ed inserisci() non prevedono parametri di ingresso con JSF ed al loro interno istanziano un oggetto della classe FileMP3 per eseguire su di esso le operazioni di aggiornamento dei tag ID3v1 ed il caricamento del file MP3 sul server. Ciò è necessario perché da una generica pagina JSP, questi metodi sono invocati attraverso il method-binding e facendo riferimento ad un backing bean associato alla classe BranoMP3. Nel caso di Struts, i due metodi prevedono un parametro di ingresso del tipo FileMP3 che viene passato dai metodi corrispondenti della BranoMP3Action. E’ quest’ultima che istanza l’oggetto FileMP3 gestendo l’aggiornamento dei tag ID3v1 ed il caricamento del file. La distinzione è resa possibile dal fatto che da una pagina JSP, viene invocata la action e non direttamente i metodi della classe BranoMP3. Infine, il metodo acquista() non ha parametri di ingresso con JSF, in quanto le informazioni sull’utente in sessione e della sua scheda prepagata vengono ricavate direttamente all’interno del metodo. Con Struts, invece, è il metodo acquista() della BranoMP3Action che si occupa di accedere a queste variabili di sessione passandole al metodo acquista() della classe BranoMP3. Tutto ciò evidenzia la riusabilità di questa classe della business-logic in altri contesti, in quanto gli oggetti Utente e SchedaPrepagata potrebbero essere generati da un meccanismo completamente diverso dalle action. 363 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per quanto riguarda la classe FileMP3, essa presenta i seguenti metodi : - upload() : esegue il caricamento del file MP3 sul server. L’implementazione è praticamente la stessa con JSF e Struts e prevede l’apertuna di uno stream di ingresso associato al file in locale, l’apertura di uno stream di uscita associato al file da creare sul server e la scrittura da uno stream all’altro per il trasferimento; - elimina() : permette di eliminare il file MP3 residente sul server. Con JSF, viene determinato il percorso del file sulla base dei parametri di ingresso, genere e nomeFileMP3. Con Struts, non è previsto alcun parametro in quanto le informazioni sul path vengono specificate dal metodo elimina() della BranoMP3Action, che poi invoca il metodo in questione; - aggiornaTag() : esegue l’aggiornamento dei tag ID3v1 del file MP3. L’implementazione è la stessa in entrambi i casi e prevede l’utilizzo degli oggetti MP3File ed ID3v1 della Java MP3 Tag Library. Per ciascuna informazione tra titolo, autore ed album, viene valutato se essa è specificata tra le informazioni del brano oppure nel corrispondente tag del file. In base al fatto che uno dei due può essere vuoto, viene eseguito il trasferimento del valore dall’uno all’altro; E’ da sottolineare che per quanto riguarda il percorso dei file MP3, esso ha una struttura standard che dipende ovviamente dal nome del file ed anche dal genere musicale. Tale struttura è la seguente : /[dirNomeWebApplication]/mp3/[dirGenere]/nomeFile.mp3 6.4.4 Package playlist Il package playlist contiene un’unica classe che implementa tutte le funzionalità che riguardano la gestione delle playlist, la classe Playlist. In entrambe le implementazioni, le proprietà della classe sono le seguenti : - id : identificativo univoco della playlist; 364 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - nome : nome assegnato alla playlist; - dataOraCreazione : data ed ora di creazione della playlist; L’implementazione con JSF prevede una proprietà in più, elimina, che indica se la playlist è stata selezionata per l’eliminazione da un elenco di playlist attraverso una checkbox. Con Struts, non è necessaria in quanto sono utilizzate le multibox come già visto nella descrizione della View. Per quanto riguarda le dipendenze con altri classi, in entrambe le implementazioni è prevista un’associazione con ComposizionePlaylistDB, mediante la quale poter gestire le operazioni di inserimento e cancellazione di un brano nella playlist e quindi la composizione della stessa. Inoltre, ci sono delle dipendenze con le classi File, BranoMP3 e PlaylistDB rispettivamente per : - creare e cancellare il file M3U con l’elenco dei brani della playlist, necessario al player MP3 per l’ascolto; - le operazioni di inserimento e cancellazione dei brani dalla playlist; - la gestione della permanenza delle informazioni associate alla playlist; La prima differenza tra le due implementazioni riguarda la dipendenza con la classe Utente, presente con JSF ma non con Struts. Nel primo caso si rende necessaria per ricavare l’utente a cui è associata la playlist, mentre nel secondo caso tale operazione viene gestita dalla action PlaylistAction. Infine, la tipica differenza è relativa all’accesso alle variabili di sessione, che con JSF viene effettuata tramite la classe FacesContext, mentre in Struts attraverso le classi HttpServletRequest e HttpSession. 365 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 76 - JSF : package playlist 366 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 77 - Struts : package playlist I metodi previsti dalla classe sono i seguenti : - crea() : permette la creazione di una playlist vuota. Con JSF, viene ricavato dalla session l’identificativo dell’utente che sta creando la playlist e viene invocato il metodo PlaylistDB.inserisci(), per poi restituire l’outcome per la navigazione. Con Struts, riceve in ingresso l’identificativo dall’utente che gli viene passato dal metodo crea() della PlaylistAction da cui viene invocato ed esegue l’inserimento nel database. E’ compito della action gestire la navigazione nel caso o meno di errori; 367 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - visualizzaInfo() : permette il caricamento delle informazioni della playlist selezionata, che restituisce come parametro di uscita. La sua implementazione è perfettamente uguale in entrambi i casi; - visualizzaElencoPlaylist() : permette di visualizzare l’elenco delle playlist relative ad un certo utente. Con JSF, ricava l’identificativo dell’utente dalla session, invoca PlaylistDB.elencoPlaylist(), salva la lista nella session e restituisce l’outcome per la navigazione. Con Struts, viene invocato dal metodo visualizzaElencoPlaylist() della PlaylistAction, dal quale riceve l’identificativo dell’utente e restituisce la lista delle playlist associate a quest’ultimo. E’ compito della action salvare l’elenco nella session e far proseguire la navigazione; - elimina(), eliminaPlaylists() : metodi che permettono di eliminare una singola playlist oppure più playlist selezionate da un elenco. In entrambi i casi, invoca una o più volte PlaylistDB.elimina(). La differenza tra le due implementazioni sta nel fatto che con JSF, l’aggiornamento della lista in sessione viene fatto all’interno di questi metodi mente con Struts, viene eseguito dai corrispondenti metodi elimina() ed eliminaPlaylists() della PlaylistAction; - eliminaM3U() : permette di eliminare i file M3U associati alle playlist di uno specifico utente. Il principio di funzionamento è lo stesso per entrambe le implementazioni : una volta noto l’identificativo dell’utente, viene ricavato l’elenco delle playlist associate. Si esegue un ciclo per scorrere tutte le playist e per ciascuna di esse ne viene cancellato il corrispondente file M3U; - visualizzaElencoBraniMP3Playlist() : permette la visualizzazione dell’elenco dei brani che compongono la playlist corrente. Con JSF, viene invocato il metodo ComposizionePlaylistDB.composizionePlaylist() e l’elenco dei brani ricavato viene salvato nella session, per poi restituire l’outcome per la navigazione. Con Struts, vine invocato dal corrispondente metodo della PlaylistAction al quale restituisce l’elenco dei brani contenuti nella playlist. E’ poi compito della action salvare la lista in sessione e far proseguire la navigazione; - inserisciBranoMP3(), inserisciBraniMP3() : permettono di inserire un singolo brano oppure più brani all’interno di una specifica playlist. In entrambe le implementazioni, prevedono l’invocazione del metodo ComposizionePlaylistDB.inserisci() una o più volte. Con JSF, all’interno di questi 368 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ metodi vengono ricavate le informazioni dei brani da inserire e restituito l’outcome per la navigazione. Con Struts, queste operazioni vengono eseguite nei metodi inserisciPlaylist() e inserisciBraniMP3Playlist() della BranoMP3Action; - eliminaBraniMP3() : effettua l’eliminazione di uno o più brani selezionati dall’elenco di composizione della playlist. In entrambe le implementazioni viene eseguito un ciclo sull’elenco dei brani selezionati per invocare il metodo ComposizionePlaylistDB.elimina(). - ascolta() : permette l’ascolto di una playlist. La sua implementazione è fondamentalmente la stessa con JSF e con Struts in quanto prevede il caricamento dei brani costituenti la playlist con il metodo ComposizionePlaylistDB.composizionePlaylist() e la creazione di un file M3U in cui scrivere i percorsi dei file MP3 associati ai brani da ascoltare; E’ da osservare che i metodi visualizzaElencoPlaylist() ed eliminaPlaylists() sono definiti statici nell’implementazione con Struts ma non con JSF. Da un punto di vista concettuale ciò è corretto, in quanto le funzionalità sono relative alle playlist in generale e non ad una specifica playlist. Con Struts, ciò è possibile in quanto dalle pagine JSP vengono invocati i metodi delle action che a loro volta utilizzano i metodi della classe Playlist. Con JSF, invece, dalle pagine JSP si invocano direttamente questi metodi utilizzando il backing bean associato a questa classe, per cui non è possibile invocare metodi statici ma soltanto metodi di istanza. E’ da precisare che il file M3U con i percorsi dei brani MP3 contenuti nella playlist, è necessario per come opera il player MP3 utilizzato. Per rendere tale file univoco, esso ha un nome caratterizzato dall’identificativo della playlist concatenato con il nome della stessa. Il percorso in cui sono salvati tali file è il seguente : /[dirNomeWebApplication]/mp3/playlist/[id+nomePlaylist].m3u 369 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ 6.4.5 Package scheda Il package scheda contiene le classi SchedaPrepagata e Ricarica che mettono a disposizione le funzionalità per la gestione della scheda prepagata e delle corrispondenti operazioni di ricarica. La classe SchedaPrepegata prevede le stesse proprietà per entrambe le implementazioni : - id : identificativo della scheda; - importoResiduo : importo presente sulla scheda; - dataOraAcquisto : data ed ora di acquisto della scheda; - dataScadenza : data di scadenza della scheda (ad un anno dall’acquisto oppure dall’ultima ricarica); Allo stesso modo, la classe Ricarica prevede le seguenti proprietà : - id : identificativo dell’operazione di ricarica; - importo : importo della ricarica; - dataOra : data ed ora in cui è stata effettuata la ricarica; Esse sono ovviamente legate fra loro, considerando che un’operazione di ricarica è sempre associata alla scheda su cui è stata effettuata. Per quanto riguarda le dipendenze con le altre classi dell’applicazione, la classe Ricarica è legata soltanto a RicaricaDB per gestire la permanenza delle operazioni di ricarica. La classe SchedaPrepagata, invece, è associata alla classe SchedaPrepagataDB per gestire la persistenza delle sue informazioni, alla classe CartaCredito per il pagamento delle operazioni di acquisto e ricarica ed alla classe Utente, essendo associata ad un utente registrato. La differenza tra le due implementazioni risiede sempre nell’accesso alle variabili di sessione, effetuato con FacesContext nel caso di JSF e direttamente con le classi HttpServletRequest ed HttpSession con Struts. 370 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 78 - JSF : package scheda 371 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 79 - Struts : package scheda 372 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Per quanto riguarda la classe SchedaPrepagata, i suoi metodi sono i seguenti : - acquista() : metodo che permette ad un utente l’acquisto di una scheda prepagata. Con JSF, esso ricava dalla session l’oggetto dell’utente che sta effettuando l’acquisto e le informazioni della carta di credito e ne verifica la copertura, calcola la data di scadenza della scheda, la inserisce nell’archivio mediante il metodo SchedaPrepagataDB.inserisci() ed infine restituisce l’outcome per la navigazione. Con Struts, viene invocato dal metodo execute() della AcquistoSchedaAction e riceve da quest’ultima, in ingresso, gli oggetti relativi all’utente ed alla carta di credito che la action ha ricavato dai form. Opera allo stesso modo dell’implementazione con JSF ma è compito della action gestire la navigazione; - visualizzaInfo() : è perfettamente identico nelle due implementazioni, poiché carica le informazioni della scheda mediante il metodo SchedaPrepagataDB.leggi() e restituisce l’oggetto SchedaPrepagata corrispondente; - ricarica() : permette di effettuare un’operazione di ricarica sulla scheda corrente. Con JSF, ricava le informazioni della carta di credito dalla session e quelle della ricarica dalla request, esegue il metodo Ricarica.esegui(), aggiorna l’importo residuo e la data di scadenza, aggiorna le informazioni nell’archivio con il metodo SchedaPrepagatDB.aggiorna() ed infine restituisce l’outcome per la navigazione. Con Struts, viene invocato dal metodo execute() della RicaricaSchedaAction dalla quale riceve gli oggetti SchedaPrepagata e Ricarica, le cui informazioni sono prelevate dalla action dai form. Opera allo stesso modo dell’implementazione con JSF ma è compito della action gestire la navigazione; - controlloImportoResiduo() : viene utilizzato in fase di acquisto di un brano per valutare se l’importo residuo è sufficiente, in relazione al costo ricevuto come parametro di ingresso. E’ perfettamente uguale nelle due implementazioni; - aggiornaImportoResiduo() : aggiorna l’importo residuo della scheda una volta effettuato l’acquisto SchedaPrepagataDB.aggiorna() di un brano. Utilizza il metodo ed è perfettamente identico nelle due implementazioni; 373 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ - controlloScadenza() : permette di valutare se la scheda sia scaduta o meno. Con JSF, effettua il confronto della data di scadenza con la data attuale e nel caso in cui la scheda sia scaduta la elimina dalla base di dati, aggiorna l’utente che non sarà più abilitato all’acquisto di brani MP3 e restituisce un valore booleano. Con Struts, effettua semplicemente il confronto tra le date ma le operazioni di eliminazione della scheda, nel caso in cui sia scaduta, ed aggiornamento dell’utente vengono eseguite dalla LoginAction in cui il metodo è invocato; Così come in altri casi, si osserva che il metodo ricarica() ha una signature diversa nelle due implementazioni. Con JSF non prevede parametri di ingresso mentre con Struts prevede l’oggetto CartaCredito e Ricarica, grazie ai quali poter effettuare l’operazione di ricarica sulla scheda. In questo modo, si evidenzia l’indipendenza della classe della business-logic dal framework, in quanto gli oggetti ricevuti in ingresso potrebbero essere generati con meccanismi completamente diversi dalle action. La classe Ricarica prevede banalmente l’unico metodo esegui() il quale è identico nelle due implementazioni e non fa altro che salvare la data e l’ora correnti e di inserire queste informazioni nell’archivio con il metodo RicaricaDB.inserisci(). 6.4.6 Package cartacredito Questo package contiene soltato la classe CartaCredito che contiene le informazioni della carta di credito utilizzata dall’utente per l’acquisto o la ricarica di una scheda prepagata. Per entrambe le implementazioni, le proprietà che la caratterizzano sono le seguenti : - tipo : tipologia di carta di credito (VISA, MasterCard, etc…); - numero : numero della carta; - codiceSegreto : codice segreto associato alla carta; - dataScadenza : data di scadenza della carta; 374 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 80 - JSF : package cartacredito Figura 81 - Struts : package cartacredito Essa non prevede una corrispondente classe per la gestione della persistenza, in quanto una volta utilizzata, le relative informazioni vengono eliminate dalla sessione. 375 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ E’ stata realizzata a scopo puramente dimostrativo infatti, il suo unico metodo verificaCopertura() restituisce sempre il valore true ad indicare che la carta di credito è coperta per il pagamento. Le uniche dipendenze previste sono con FacesContext in JSF e le classi HttpServletRequest e HttpSession in Struts per semplificare l’accesso in sessione. 6.4.7 Package download Il package download contiene la classe Download attraverso la quale si tiene traccia dell’acquisto di un brano MP3. La classe prevede l’unica proprietà dataOraAcquisto che rappresenta la data e l’ora dell’acquisto del brano, nonché è caratterizzata da un unico metodo, inserisci(), il quale non fa altro che invocare DownloadDB.inserisci() per introdurre nell’archivio l’informazione relativa all’acquisto/download di un brano da parte di un certo utente. Essa è perfettamente identica in entrambe le implementazioni. Figura 82 - JSF e Struts : package download 6.4.8 Package mail Il package mail contiene solo la classe Mail la quale è perfettamente identica in entrambe le implementazioni, non ha alcuna proprietà e prevede soltanto il metodo sendMessage() mediante il quale è possibile inviare una mail. Esso utilizza la classe Properties per fissare nelle proprietà del sistema l’host che dovrà occuparsi dell’invio e la classe Session per stabilire una sessione con il mail server. Genera un messaggio del tipo MimeMessage definendone il mittente, il destinatario, l’oggetto ed il 376 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ corpo che riceve come parametri di ingresso. Infine, invia il messaggio invocando il metodo Transport.send(). Figura 83 - JSF e Struts : package mail 6.5 I Backing Beans di JavaServer Faces Nell’implementazione realizzata utilizzando JavaServer Faces, tutte le classi che costituiscono il Model sono dichiarate all’interno del file di configurazione come backing beans (managed beans). Questo tipo di approccio garantisce due vantaggi fondamentali : - ciascun componente dell’interfaccia utente può essere associato ad una proprietà di un backing bean attraverso il value-binding, in modo tale che quando il form a cui appartiene il componente viene trasmesso, il valore che assume il componente viene trasferito nella corrispondente proprietà a cui è legato; - utilizzando gli eventi , action e value-change, è possibile invocare direttamente da un componente un metodo di un backing bean, attraverso il method-binding, in modo da poter effettuare delle elaborazioni sui dati contenuti nel backing bean stesso; 377 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ In base a quanto detto, è evidente la netta differenza rispetto al framework Struts, per quanto riguarda l’acquisizione dei dati dai form e l’esecuzione delle elaborazioni su di essi. Infatti, Struts utilizza i formbean le cui proprietà sono associate ai campi di un form in modo che, una volta che quest’ultimo sia stato trasmesso, i valori dei campi stessi vengano copiati nel formbean corrispondente. Sarà compito della action che riceve il formbean copiare tali dati nelle corrispondenti proprietà di un oggetto del Model, per poterne eseguire una qualsiasi elaborazione. JSF evita questa passaggio intermedio facendo in modo che i valori dei campi dei form vengano copiati direttamente nelle corrispondenti proprietà di un backing bean. Per quanto riguarda l’avvio delle elaborazioni da una pagina JSP, Struts prevede il concetto di action che viene invocata ad esempio alla pressione di un bottone oppure di un link. La action riceve un formbean con i dati da elaborare, li trasferisce nelle corrispondenti proprietà di un oggetto del Model e su di esso invoca un metodo per eseguire l’elaborazione richiesta. JSF permette di avviare direttamente da una pagina JSP un metodo di un backing bean, ossia di un oggetto del Model, nel quale ci saranno automaticamente i dati da elaborare. Anche in questo caso, si evita un passaggio intermedio che riguarda l’utilizzo delle action di Struts. All’interno del file di configurazione, faces-config.xml, è possibile dichiarare opportunamente ciascun backing bean specificandone il tipo, ossia la classe che lo implementa, e l’ambito di visibilità (scope) per indicare se sia accessibile soltanto nell’ambito di una richiesta (request), di una sessione utente (session) oppure nel contesto della servlet (application). In maniera del tutto automatica è compito del framework gestire l’allocazione e la deallocazine dei backing bean quando necessario, grazie al meccanismo dell’ IoC (Inversion of Control) noto anche come DI (Dependency Injection). Un semplice esempio è la seguente dichiarazione del backing bean relativo all’utente, al quale viene associato un nome, ne viene specificata la classe di appartenenza, ruolo.Utente, ed infine indicato l’ambito di visibilità, session, in modo che le informazioni dell’utente siano sempre accessibili durante la sua sessione di utilizzo dell’applicazione. 378 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ <managed-bean> <managed-bean-name>utente</managed-bean-name> <managed-bean-class>ruolo.Utente</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> Per completare il confronto tra JSF e Strus, relativo a questo aspetto, si prenda in considerazione l’operazione di login. Nel caso di JSF, è stato dichiarato il backing bean ruolo con un’ambito di visibilità strettamente legato alla richiesta. <managed-bean> <managed-bean-name>ruolo</managed-bean-name> <managed-bean-class>ruolo.Ruolo</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> La pagina JSP, contenente il form per effettuare l’accesso al sistema, prevede due campi di testo associati alle proprietà userName e password del backing bean suddetto, così come al bottone di invio è associata l’esecuzione del relativo metodo login(). ... <h:outputText value="#{bundle.menuUsernameLabel}" /> <h:inputText id="userName" value="#{ruolo.userName}"/> <h:outputText value="#{bundle.menuPasswordLabel}" /> <h:inputSecret id="password" value="#{ruolo.password}"/> <h:outputText value="" /> <h:commandButton action="#{ruolo.login}" value="#{bundle.menuButtonLoginLabel}" styleClass="buttonForm"/> ... Struts prevede un’implementazione completamente diversa, in cui nel file di configurazione è dichiarato il formbean loginForm, del tipo LoginForm, associato alla action LoginAction. ... <form-bean name="loginForm" type="formbean.LoginForm"/> ... <action input="/pages/home.jsp" name="loginForm" path="/login" scope="request" type="defaultactions.LoginAction"> <forward name="failure" path="/pages/home.jsp"/> </action> ... 379 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ La pagina JSP prevede un form con due campi di testo associati alle proprietà del formbean ed un bottone che avvia l’esecuzione della action. <html:form action="/login.do"> ... <tr> <td class="columsLabelForm"> <fmt:message key="menuUsernameLabel"/> </td> <td class="columsInputForm"> <html:text property="userName" /> </td> </tr> <tr> <td class="columsLabelForm"> <fmt:message key="menuPasswordLabel"/> </td> <td class="columsInputForm"> <html:password property="password" /> </td> </tr> ... <html:submit styleClass="buttonForm"> <fmt:message key="menuButtonLoginLabel"/> </html:submit> </html:form> All’interno della action ci sarà il codice che copia i dati del form dal formbean all’oggetto corrispondente della business-logic, per poi invocare il metodo login() su quest’ultimo. ... // ricavo le informazioni sul ruolo che tenta di accedere Ruolo ruolo = new Ruolo(); formToRuolo(form,ruolo); ... ... // invoco il login sul Ruolo ruolo = ruolo.login(); 7. Plug-in di Struts Il framework Struts prevede un particolare meccanismo per poter estendere le sue funzionalità legato al concetto di plug-in. Basti pensare al Validator ed allo stesso Tiles che vengono dichiarati all’interno del file di configurazione appunto come plug-in. Ovviamente, lo sviluppatore ha la possibilità di realizzarne uno proprio 380 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ mediante una classe che implementi l’interfaccia PlugIn. Il loro uso può risultare utile quando c’è la necessità di eseguire una certa operazione una sola volta ed all’avvio dell’applicazione. Proprio questa modalità di utilizzo è stata adottata nell’implmenetazione con Struts, realizzando il plug-in StartupManager che, una volta avviato, crea le liste di items che verrano usate in tutte le drop-down list dell’applicazione, ad esempio quella relativa alle province, agli stati e così via. Figura 84 - Struts : StartupManager Tale classe prevede i due tipici metodi di un plug-in : - initi() : viene eseguito all’avvio dell’applicazione e può contenere tutte le operazioni necessarie allo startup della Web application; - destroy() : viene invocato alla chiusura dell’applicazione per “distruggere” il plug-in; Il plug-in realizzato prevede inoltre la proprietà sc che è un’istanza della classe ServletContext, in quanto è all’interno di quest’ultima che verranno salvate le liste generate. Infatti, ogni oggetto salvato all’interno del contesto della servlet ha un ambito di visibilità di tipo application ed è quindi accessibile in un qualsiasi punto dell’applicazione ed è comune a tutti gli utenti. 381 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ La generazione delle liste viene eseguita nel metodo init(), cos’ come il salvataggio nel contesto della servlet, invocando il metodo setAttribute() sulla proprietà sc. Infine, il plug-in è stato dichiarato nell’opportuna sezione all’interno del file di configurazione. <plug-in className="plugin.StartupManager"/> Nell’implementazione con JSF, non essendo disponibile uno strumento come i plugin di Struts, è stato realizzata la classe ItemsBean per la quale ne è stato dichiarato un backing bean nell’ambito di visibilità application. Figura 85 - JSF : ItemsBean Si osservi che le proprietà della classe corrispondono esattamente alle liste necessarie all’interno dell’applicazione. La generazione delle liste viene eseguita all’interno del costruttore e quindi nel momento in cui il framework istanzia questo backing bean. 8. Navigazione La navigazione all’interno dell’applicazione è certamente uno degli aspetti che differenzia maggiormente i due framework, in quanto essi sfruttano due meccanismi diversi che comunque possono essere paragonati l’uno all’altro. JavaServer Faces si basa sul concetto delle navigation-rules, all’interno di ciascuna delle quali va definita la pagina di partenza della specifica “regola” e vanno definiti uno o più navigation-case che specificano, sulla base dell’outcome rinvenuto dal NavigationHandler, quale sia la pagina verso la quale proseguire la navigazione. 382 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ In base a quanto detto, ogni regola prevede : - la pagina che rappresenta il punto di partenza di riferimento; - uno o più pagine destinazione che è possibile raggiungere dalla suddetta pagina, ciascuna delle quali è distinta da un differente outcome; Ciò vuol dire che, nel momento in cui viene invocato un metodo di un backing bean da una certa pagina JSP, quest’ultimo dovrà restituire come parametro di uscita una stringa che rappresenti l’outcome di navigazione. Sulla base di quest’ultimo, il NavigationHandler esegue una ricerca nel file di configurazione e determina verso quale pagina deve proseguire la navigazione. Nel caso della Web application implementata si è evidenziata la non perfetta integrazione tra JSF e Tiles. Infatti, se si utilizza questo framework per la definizione del layout, non è possibile utilizzare le regole di navigazione come spiegato in precedenza, ma è necessario definire un’unica regola, senza alcuna pagina associata, all’interno della quale ci sono tutti i navigationcase. <navigation-rule> <navigation-case> <from-outcome>registrazione</from-outcome> <to-view-id>/pages/registrazione.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>home</from-outcome> <to-view-id>/pages/home.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>acquistoRicaricaScheda</from-outcome> <to-view-id>/pages/acquistoRicaricaScheda.jsp</to-view-id> </navigation-case> ... </navigation-rule> Il motivo di questo limite è dato dal fatto che nel Deployment Descriptor web.xml dell’applicazione realizzata con JSF, l’utilizzo di Tiles è specificato attraverso la dichiarazione di un’ulteriore servlet, TilesServlet, che viene avviata immediatamente dopo la FacesServlet e che intercetta per prima le richieste verso le pagine, interferendo con il NavigationHandler. Se in JSF la definizione della navigazione viene dichiarata in termini generali, con Struts è descritta in maniera più specifica e localizzata, all’interno delle action 383 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ mediante il concetto di forward. Infatti, ciascuna action prevista dall’applicazione può avere un unico metodo execute() oppure più metodi, nel caso di una DispatchAction, che comunque restituiscono un oggetto del tipo ActionForward. Ciascun forward specifica la pagina verso la quale proseguire la navigazione ed è dichiarato all’interno del file di configurazione, secondo due modalità : - locale : è relativo ad una specifica action che è l’unica che permette la navigazione verso la pagina ad esso associata; - globale : è comune a tutte le action, in modo tale che ciascuna di esse possa redirezionare il flusso dell’applicazione verso la pagina ad esso associata; <action input="/pages/registrazione.jsp" name="registrazioneForm" path="/registrazione" scope="session" type="defaultactions.RegistrazioneAction" validate="true"> <forward name="login" path="/login.do"/> <forward name="acquistoScheda" path="/pages/acquistoScheda.jsp"/> <forward name="failure" path="/pages/registrazione.jsp"/> </action> In un certo senso, il nome del forward restituito può essere paragonato all’outcome di JSF e ciascun forward può essere equiparato ad un navigation-case così come la dichiarazione della action ad una navigation-rule. 9. Eccezioni La gestione delle eccezioni dichiarative è una potenzialità messa a disposizione solo ed esclusivamente da Struts ma non da JSF. Infatti, con Struts, è possibile dichiarare un’eccezione nel file di configurazione in modo tale che, nel caso in cui venga sollevata all’interno del codice, entri in gioco in maniera automatica l’ExceptionHandler che si fa carico di gestirla. Inoltre, all’eccezione stessa è possibile associare una pagina verso la quale redirezionare il flusso dell’applicazione. <global-exceptions> <exception bundle="ApplicationResources" key="errorDatabase" path="/pages/error.jsp" type="org.apache.struts.util.ModuleException"/> </global-exceptions> 384 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Nel caso di studio in esame, è stato previsto l’utilizzo delle eccezioni dichiarative per poter visualizzare all’utente una pagina di errore, qual’ora si verificasse qualche problema di accesso alla base di dati. Moltissime applicazioni, in questi casi, prevedono semplicemente la visualizzazione di errori di accesso assolutamente incomprensibili all’utente. Per evitare una situazione di questo tipo, si è fatto in modo che, se durante l’accesso al database si verifica un errore, viene sollevata una ModuleException la quale è intercettata dall’ExceptionHandler che la ricerca nel file di configurazione e fa in modo che all’utente venga visualizzata la pagina ad essa associata. ... } catch (SQLException e) { ModuleException me = new ModuleException("errorDatabase"); throw me; } finally { ... JavaServer Faces non fornisce questa potenzialità per cui in questo caso si è ricorso alla semplice funzionalità di navigazione. Più precisamente, nel caso in cui si verifichi un errore di accesso alla base di dati, viene sollevata un’eccezione che, intercettata all’interno del codice stesso, fa in modo che il metodo del backing bean in questione restituisca l’outcome verso la pagina di errore. 10. Sicurezza La Web application realizzata prevede una funzionalità particolare, nell’ambito della quale sono trasmessi dei dati fortemente sensibili, quali il numero ed il codice della carta di credito, per l’acquisto e la ricarica di una scheda prepagata. In una situazione di questo tipo, si rende necessario l’utilizzo di un meccanismo di sicurezza mediante il quale sia possibile crittografare i dati inviati attraverso la rete, in modo da renderli incomprensibili a chiunque riesca ad intercettarli in maniera fraudolenta. 385 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Gli elementi principali che permettono un approccio di questo tipo sono fondamentalmente due : - l’acquisizione di un certificato digitale; - uso del protocollo HTTPS, ossia HTTP basato su SSL (Secure Socket Layer); Attraverso un certificato digitale, rilasciato dalla CA (Certification Authority), è sempre possibile essere a conoscenza dell’identità dell’interlocutore e decidere se accettare o meno di stabilire una comunicazione con qeust’ultimo. Uno strumento di questo tipo risulta notevolmente utile, in virtù del fatto che ci si potrebbe ritrovare a scambiare informazioni con un’entità sulla rete che non è quella da noi attesa, ma che ne abbia preso il posto in maniera fraudolenta. Chiunque sia in possesso di un certificato digitale è stato rinosciuto da terze parti e quindi si ha la certezza che i dati trasmessi verranno acquisiti dal destinatario corretto. Una delle funzionalità che è possibile sfruttare mediante i certificati digitali è la crittografia, mediante la quale è possibile cifrare i dati trasmessi e renderli illeggibili durante il loro invio. Soltanto il destinatario, riconosciuto sulla base di un certificato digitale, avrà la possibilità di decrittografarli attraverso l’utilizzo di una chiave. La comunicazione avviene attraverso il protocollo HTTPS, ossia il tipico HTTP basato su SSL (Secure Socket Layer), che garantisce una cifratura con chiave a 128 bit. Nel caso di studio in esame, per garantire la sicurezza, si sono rese necessarie le seguenti operazioni : - creazione di un certificato digitale di esempio; - abilitazione del Connector SSL nel Web Container Apache Tomcat; - configurazione della pagine protette nel Deployment Descriptor dell’applicazione; La creazione del certificato digitale è stata effettuta facendo uso di uno degli strumenti messi a disposizione dall’SDK di Java2, ossia keytool, che produce un cosiddetto keystore. Quest’ultimo permette inoltre di specificare il tipo di algoritmo da utilizzare per la crittografia, che nel caso specifico è l’RSA. 386 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ keytool -genkey -alias tomcat -keyalg RSA Una volta creato il certificato, bisogna abilitare il Connector SSL del Web Container Tomcat, mediante il quale è possible redirezionare tutte le comunicazioni protette, verso una porta specificata e tramite protocollo HTTP. Per fare questo è necessario apportare una modifica al file di configurazione server.xml, all’interno del quale va specificato il keystore da utilizzare e la relativa password. <Connector port="8443" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" acceptCount="100" debug="0" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystorePass="changeit" keystoreFile="C:\Documents and Settings\Paolo\.keystore"/> Per quanto riguarda la modifica da applicare alla Web application, essa è del tutto analoga in entrambe le implementazioni, tenendo conto del fatto che JSF e Struts demandano la gestione della sicurezza al Web Container sottostante e non forniscono un proprio particolare strumento. All’interno del Deployment Descriptor web.xml è possibile specificare le pagine, la cui trasmissione deve essere effettuata su protocollo HTTPS. In questo caso si è deciso di rendere tutta l’applicazione sicura, garantendo la trasmissione tramite SSL per tutte le pagine a partire dalla pagina di ben venuto index.jsp. <security-constraint> <display-name>SSL Constraint</display-name> <web-resource-collection> <web-resource-name> Automatic SLL Forwarding </web-resource-name> <url-pattern>/index.jsp</url-pattern> <http-method>GET</http-method> <http-method>PUT</http-method> <http-method>POST</http-method> <http-method>DELETE</http-method> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> 387 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ In questo modo, alla richiesta dell’URL iniziale viene segnalata la redirezione verso una connessione sicura garantita da un certificato digitale. La trasmissione dei dati tra client e server avverrà tramite il protocollo SSL in modalità crittografata. Nella figura seguente, è visibile il messaggio del browser Web che segnala il passaggio ad una modalità di comunicazione protetta mediante protocollo SSL. E’ possibile visionare le informazioni relative al certificato digitale, in modo da essere a conoscenza dell’interlocutore e decidere se accettare o meno la trasmissione dei dati. Figura 86 - Segnalazione del certificato digitale Una volta accettato il passaggio al protocollo HTTPS, la navigazione all’interno della Web application prosegue normalmente, ma è visibile nella parte bassa del browser (es. Internet Explorer 6) un lucchetto che indica la garanzia di una comunicazione protetta e cifrata. 388 Capitolo VI – Case Study : Implementazioni a confronto _________________________________________________________________ Figura 87 - Indicazione modalità protetta con SSL 389 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ Appendice A – SRS Web Application MP3-Web 1. Introduzione 1.1 Propositi Questo documento ha lo scopo di definire i requisiti e le specifiche del prodotto software “MP3 - Web”, al fine di facilitarne la realizzazione e la validazione. E’ destinato sia al committente del prodotto software che allo sviluppatore, al fine di definire una base di riferimento per la validazione del prodotto e creare le premesse per la produzione di un software di alta qualità. 1.2 Obiettivi Il prodotto software “MP3 - Web” ha come obiettivo la realizzazione di una Web application che permetta agli utilizzatori di usufruire di contenuti audio, quali brani musicali in formato MP3 (MPEG Layer 3). Il sistema permette l’automatizzazione delle seguenti operazioni, per quanto riguarda l’interazione con l’Utente : 1. Registrazione nelle versioni “base” ed “estesa”; 2. Identificazione dell’Utente tramite lo Username e la Password; 3. Accesso all’archivio dei brani MP3; 4. Ricerche avanzate nell’archivio dei brani MP3; 5. Ascolto di un singolo brano MP3; 6. Gestione ed ascolto di playlist personalizzate contenenti brani MP3; 7. Acquisto e ricarica di una scheda prepagata per effettuare il download dei brani MP3; 8. Acquisto e Download dei brani MP3; 9. Gestione dei propri dati personali; 10. Richiesta dei propri dati di accesso via mail; 11. Uscita dal sistema; 390 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ L’Amministratore disporrà di funzionalità avanzate, mediante le quali saranno consentite le seguenti operazioni : 1. Identificazione dell’Amministratore tramite lo Username e la Password; 2. Gestione dell’archivio dei brani MP3 per l’accesso, la ricerca, l’inserimento, la modifica e l’ascolto; 3. Gestione degli utenti registrati; 4. Gestione dei propri dati di accesso; 5. Uscita dal sistema; 1.3 Definizioni, Acronimi ed Abbreviazioni Utente : persona che ha effettuato la registrazione (“base” oppure “estesa”) e che risulta abilitata all’utilizzo dei servizi resi disponibili dalla scelta effettuata; Amministratore : persona addetta alla gestione degli archivi degli utenti e alla gestione dei servizi offerti dal sistema; Utilizzatore – Ruolo : persona non ancora identificata tra Utente ed Amministratore, che non ha effettuato l’accesso al sistema; Servizio : ciascuna delle attività accessibili e consentite ad un qualunque tipo di utente o all’amministratore; Registrazione : contratto stipulato dall’Utente, necessario per poter usufruire dei servizi offerti dal sistema. Può essere di due tipologie : - base : permette di accedere a tutti i servizi a meno del download di brani MP3; 391 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ - esteso : offre i medesimi servizi della registrazione “base” ed in più permette l’acquisto ed il download di brani MP3, previo l’acquisto di una scheda prepagata; Username : identificativo univoco dell’Utente oppure dell’Amministratore; Password : password di accesso al sistema dell’Utente oppure dell’Amministratore; Playlist : sequenza di ascolto dei brani musicali definita dall’Utente; 1.4 Riferimenti Standard IEEE 830-1993. 1.5 Generalità L’intento di questo documento è quello di descrivere le funzionalità che il software deve soddisfare, le quali saranno specificate nel capitolo successivo in modo chiaro e conciso. Il resto del documento contiene l’elenco delle funzioni che il prodotto software deve avere insieme ai vincoli che deve soddisfare. Più avanti nel testo sono presentate l’interfaccia (sia verso l’Utente sia verso l’Amministratore) dei servizi messi a disposizione dal prodotto e l’interfaccia verso l’hardware. Al presente documento ne sono allegati altri contenenti i diagrammi realizzati secondo lo standard UML, necessari per la miglior comprensione del dominio applicativo. 392 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 2. Descrizione Generale 2.1 Prospettive del prodotto Questo prodotto non si integra con nessun altro sistema software ed è indipendente ed autonomo per quanto riguarda l’elaborazione(stand-alone). 2.2 Funzionalità Il prodotto software “MP3 - Web” dovrà : nei confronti dell’Amministratore fornire le seguenti funzionalità : - accesso al sistema; - registrazione dell’accesso; - modifica della propria Password di accesso; - gestione dell’archivio di brani MP3, che prevede : - - visualizzazione dei brani sulla base di un genere musicale; - visualizzazione dei brani sulla base di criteri di ricerca specifici; - inserimento di un nuovo brano in archivio; - modifica delle informazioni di un brano; - eliminazione di uno o più brani dall’archivio; - ascolto di un brano; gestione degli utenti registrati, che prevede : - eliminazione di un utente; - sblocco di un utente bloccato; - uscita dal sistema; - registrazione dell’uscita; nei confronti dell’Utente fornire le seguenti funzionalità : - registrazione; - richiesta via mail dei dati di accesso; - accesso al sistema; - registrazione dell’accesso; 393 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ - modifica dei propri dati personali; - accesso all’archivio dei brani, che prevede : - - - visualizzazione dei brani sulla base di un genere musicale; - visualizzazione dei brani sulla base di criteri di ricerca specifici; - ascolto di un brano; - acquisto e download di un brano; gestione di playlist personalizzate, che prevede : - visualizzazione delle playlist già create in precedenza; - creazione di una nuova playlist; - eliminazione di una o più playlist; - inserimento ed eliminazione di brani da una playlist; - ascolto di una playlist; - acquisto e download di un brano presente in una playlist gestione di una propria scheda prepagata, che prevede : - acquisto di una scheda prepagata; - ricarica della propria scheda prepagata; - uscita dal sistema; - registrazione dell’uscita; 2.3 Caratteristiche Utente Il prodotto software è destinato ad utenti che abbiano una conoscenza di base nell’utilizzo di Personal Computer e relativi software per la produttività personale; mentre gli amministratori, che usano il software di gestione, hanno bisogno di una conoscenza informatica di base. 394 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 2.4 Vincoli Generali I vincoli generali sono i seguenti : - L’Utente che richiede l’accesso ai servizi del sistema deve digitare esclusivamente il proprio Username e la relativa Password; - L’Utente non può inserire più di 2 volte una Password errata; - All’Utente non è permesso l’acquisto oppure la ricarica di una scheda prepagata, se il credito su conto corrente, cui fa riferimento la carta di credito per il pagamento, è insufficiente. - L’Utente non può acquistare ed effettuare il relativo download di un brano MP3 se non è in possesso di una scheda prepagata oppure se il credito residuo sulla scheda prepagata è insufficiente; - L’Amministratore non può inserire più di 2 volte una Password errata; - L’Amministratore che richiede l’accesso ai servizi del sistema deve digitare esclusivamente il proprio Username e la relativa Password; - La Password di accesso dell’Utente o dell’Amministratore può essere composta da almeno 5 caratteri alfanumerici; 2.5 Assunzioni e Dipendenze Il Sistema software dovrà essere utilizzato su una macchina dotata di sistema operativo Windows 95/98/Me/2000/Xp oppure di una distribuzione Linux, con 128 Mb di memoria RAM, Hard Disk da 10 GB ed una connessione Internet consigliata ADSL. 395 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3. Requisiti Specifici 3.1 Requisiti di interfaccia esterna 3.1.1 Interfaccia Utente E’ richiesta un’interfaccia utente visuale, con finestre, bottoni e form di immissione dati. 3.1.2 Interfaccia Hardware Il sistema software non si interfaccia con alcun particolare sistema hardware. 3.1.3 Interfaccia Software Il sistema software non si integra e non comunica con nessun altro sistema software. 3.1.4 Interfaccia di Comunicazione La comunicazione fra il sistema software “lato client” e quello “lato server”, si svolge utilizzando la rete Internet e si basa fondamentalmente sull’accesso di un database condiviso localizzato sul server stesso. 396 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2 Requisiti Funzionali Classe Amministratore 3.2.1 Login 3.2.1.1 Introduzione Questa funzionalità permette all’Amministratore di accedere al sistema. 3.2.1.2 Input • Username (obbligatorio) • Password (obbligatorio) 3.2.1.3 Elaborazione Visualizzazione di un form per l’input dei dati, sui quali il sistema esegue le seguenti operazioni : • Controlla che la Username e la Password siano corrette e cioè corrispondano a quelle memorizzate nell’archivio. Se il controllo va a buon fine, viene permesso l’accesso, altrimenti viene concesso un altro tentativo ed in caso di un ulteriore errore, viene registrato l’accaduto e bloccato l’accesso al sistema; • Controlla che l’Amministratore non sia già loggato; • Registrazione dei dati relativi all’accesso; • Verifica dei diritti di accesso; • Visualizzazione del menù per accedere alle aree di gestione; 3.2.1.4 Output Menu di accesso alle aree di gestione. Dati relativi all’accesso registrati in archivio : • Data ed Ora di login • Indirizzo IP • Validità login • Tentativo 397 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ Messaggi : “Username e/o Password errati” “Tentativi di accesso esauriti : sistema bloccato” “Già loggato” 3.2.2 Modifica Password Amministratore 3.2.2.1 Introduzione Questa funzionalità permette all’Amministratore di modificare i propri dati di accesso al sistema, nella fattispecie soltanto la password. E’ ovviamente previsto che l’Amministratore abbia già eseguito il login, altrimenti la funzionalità non è disponibile. 3.2.2.2 Input • Identificativo Amministratore (obbligatorio) • Password nuova (obbligatorio) • Conferma Password nuova (obbligatorio) 3.2.2.3 Elaborazione Visualizzazione di un form per l’input dei dati, sui quali il sistema esegue le seguenti operazioni : • Controlla che la Password nuova e Conferma Password siano almeno di 5 caratteri e visualizza eventualmente un messaggio di errore; • Controlla che la Password nuova e la Conferma Password coincidano, altrimenti visualizza un messaggio di errore; • Registrazione dei nuovi dati inseriti; 3.2.2.4 Output Parte dei dati letti in input che vengono registrati in archivio : • Password nuova; Messaggi : “La Password nuova e la sua Conferma non coincidono” 398 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ “La Password deve avere una lunghezza di almeno 5 caratteri” “La Conferma Password deve avere una lunghezza di almeno 5 caratteri” “Aggiornamento effettuato con successo” 3.2.3 Visualizzazione elenco brani MP3 per genere musicale 3.2.3.1 Introduzione Questa funzionalità permette all’Amministratore di visualizzare l’elenco dei brani MP3 presenti in archivio, relativamente ad un determinato genere musicale 3.2.3.2 Input • Genere Musicale (obbligatorio) 3.2.3.3 Elaborazione Visualizzazione di un form con l’elenco dei generi musicali disponibili, sulla base del quale, effettuando una scelta, il sistema effettua le seguenti operazioni : • Controlla che sia stato selezionato un Genere Musicale; • Caricamento dei dati presenti in archivio; • Visualizzazione dell’elenco dei brani MP3 sulla base del genere musicale scelto; 3.2.3.4 Output Elenco dei brani musicali relativi al genere scelto Messaggi : “Nessun genere musicale selezionato” “Nessun brano disponibile in archivio per questo genere” 399 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.4 Ricerca di un brano MP3 secondo uno o più criteri 3.2.4.1 Introduzione Questa funzionalità permette all’Amministratore di cercare uno o più brani MP3 all’interno dell’archivio, sulla base di propri criteri, per poter effettuare su di essi delle eventuali operazioni. 3.2.4.2 Input • Titolo • Autore • Genere Musicale • Album 3.2.4.3 Elaborazione Visualizzazione di un form che permette all’Amministratore di stabilire i propri criteri di ricerca dei brani MP3. Inoltre, il sistema effettua le seguenti operazioni : • Controlla che almeno uno fra i campi Titolo, Autore, Genere Musicale e Album non sia vuoto; • Caricamento dei dati presenti in archivio; • Visualizzazione dell’elenco dei brani MP3; 3.2.4.4 Output Elenco dei brani musicali che corrispondono ai criteri impostati. Messaggi : “Nessun brano corrisponde ai criteri impostati” “Non è stato impostato alcun criterio di ricerca” 400 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.5 Inserimento di un brano MP3 3.2.5.1 Introduzione Questa funzionalità permette all’Amministratore di inserire un nuovo brano MP3 in archivio. Le informazioni relative al brano possono essere specificate dall’Amministratore oppure vengono lette dal sistema direttamente dal file MP3 caricato sul server, tramite i tag MP3. 3.2.5.2 Input • Titolo • Autore • Genere Musicale (obbligatorio) • Album • Durata • Costo (obbligatorio) • File MP3 del brano (obbligatorio) 3.2.5.3 Elaborazione Visualizzazione di un form per l’input dei dati sui quali il sistema esegue le seguenti operazioni : • Controlla che sia stato selezionato un File MP3 da caricare nel sistema; • Nel caso in cui l’Amministratore non abbia specificato le informazioni del brano, queste vengono estratte direttamente dal File MP3 caricato, tramite i tag MP3 e si controlla comunque che alcuni tag MP3 non siano specificati; • Verifica che ci sia coerenza tra le informazioni immesse dall’Amministratore ed i tag MP3; • Registrazione dei dati di input in archivio; 3.2.5.4 Output Tutti i dati letti in input registrati in archivio. Altri dati registrati in archivio : • Dimensione (in byte) 401 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ Messaggi : “Inserimento brano effettuato con successo” “Il tag relativo al titolo è vuoto” “Il tag relativo al genere musicale è vuoto” “Il tag relativo all’autore è vuoto” 3.2.6 Visualizzazione informazioni di un brano MP3 3.2.6.1 Introduzione Questa funzionalità permette all’Amministratore di visualizzare le informazioni dettagliate di un brano MP3 presente in archivio. 3.2.6.2 Input • Identificativo brano MP3 (obbligatorio) Dati caricati dal sistema provenienti dall’archivio. 3.2.6.3 Elaborazione • Caricamento dei dati presenti in archivio; • Visualizzazione delle informazioni dettagliate del brano selezionato; 3.2.6.4 Output Informazioni dettagliate del brano selezionato. 3.2.7 Modifica informazioni di un brano MP3 3.2.7.1 Introduzione Questa funzionalità permette all’Amministratore di modificare le informazioni di un brano MP3 presente in archivio, garantendo la coerenza con i tag MP3 del brano stesso. 3.2.7.2 Input • Titolo • Autore • Genere Musicale (obbligatorio) 402 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Album • Durata • Costo (obbligatorio) 3.2.7.3 Elaborazione Visualizzazione di un form, contenente i dati attuali relativi al brano MP3, caricati dall’archivio, per l’input dei nuovi dati sui quali il sistema esegue le seguenti operazioni: • Controlla che i campi relativi a Genere Musicale e Costo non siano vuoti; • Aggiornamento dei tag MP3 del brano per garantire la coerenza con le informazioni in archivio; • Registrazione dei dati di input in archivio; 3.2.7.4 Output Tutti i dati letti in input registrati in archivio. Messaggi : “Aggiornamento effettuato con successo” “Non è stato selezionato alcun genere musicale” 3.2.8 Cancellazione di un brano MP3 3.2.8.1 Introduzione Questa funzionalità permette all’Amministratore di cancellare uno o più brani MP3 presenti in archivio. 3.2.8.2 Input • Identificativo brano MP3 (obbligatorio) 3.2.8.3 Elaborazione Visualizzazione di un form, contenente l’elenco dei brani MP3 per genere musicale oppure sulla base di una specifica ricerca, con possibilità di selezionare i brani da cancellare. E’ possibile cancellare un brano anche 403 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ dalla funzionalità di visualizzazione delle sue informazioni dettagliate. Inoltre, il sistema effettua le seguenti operazioni : • Controlla che sia stato selezionato almeno un brano MP3; • Eliminazione dei dati presenti in archivio relativi al/i brano selezionato/i; • Visualizzazione dell’elenco dei brani; 3.2.8.4 Output Elenco dei brani. Messaggi : “Eliminazione effettuata con successo” “Non è stato selezionato alcun brano” 3.2.9 Ascolto di un brano MP3 3.2.9.1 Introduzione Questa funzionalità permette all’Amministratore di ascoltare in streaming un brano MP3. 3.2.9.2 Input • Identificativo brano MP3 (obbligatorio) 3.2.9.3 Elaborazione Visualizzazione di un form per l’avvio della riproduzione del brano MP3 selezionato. Inoltre, il sistema effettua le seguenti operazioni : • Controlla che sia stato selezionato un brano MP3 da ascoltare; • Caricamento del player MP3 per la ricezione in streaming e la riproduzione del brano; 3.2.9.4 Output Messaggi : “Non è stata selezionata alcun brano da ascoltare” 404 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.10 Visualizzazione elenco utenti 3.2.10.1 Introduzione Questa funzionalità permette all’Amministratore di visualizzare l’elenco degli utenti iscritti. 3.2.10.2 Input Dati caricati dal sistema provenienti dall’archivio. 3.2.10.3 Elaborazione • Caricamento dei dati presenti in archivio; • Visualizzazione dell’elenco degli utenti; 3.2.10.4 Output Elenco degli utenti. Messaggi : “Nessun utente registrato” 3.2.11 Cancellazione di un utente 3.2.11.1 Introduzione Questa funzionalità permette all’Amministratore di cancellare uno o più utenti presenti in archivio. 3.2.11.2 Input • Identificativo Utente (obbligatorio) 3.2.11.3 Elaborazione Visualizzazione di un form, contenente l’elenco degli utenti, con possibilità di selezionare gli utenti da cancellare. E’ possibile cancellare un utente anche dalla funzionalità di visualizzazione dei suoi dati anagrafici. Inoltre, il sistema effettua le seguenti operazioni : • Controlla che sia stato selezionato almeno un utente; • Eliminazione dei dati presenti in archivio relativi all/gli utente/i selezionato/i; 405 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Visualizzazione dell’elenco degli utenti; 3.2.11.4 Output Elenco degli utenti. Messaggi : “Eliminazione effettuata con successo” “Nessun utente è stato selezionato” 3.2.12 Visualizzazione dati anagrafici utente 3.2.12.1 Introduzione Questa funzionalità permette all’Amministratore di visualizzare i dati anagrafici di un utente presente in archivio. 3.2.12.2 Input • Identificativo Utente (obbligatorio) Dati caricati dal sistema provenienti dall’archivio. 3.2.12.3 Elaborazione • Caricamento dei dati presenti in archivio; • Visualizzazione dei dati anagrafici dell’utente selezionato; 3.2.12.4 Output Dati anagrafici dell’utente selezionato. 3.2.13 Sblocco di un utente 3.2.13.1 Introduzione Questa funzionalità permette all’Amministratore di sbloccare un Utente che ne abbia fatto richiesta ufficiale via telefono o via mail. 3.2.13.2 Input • Identificativo Utente (obbligatorio) 3.2.13.3 Elaborazione • Registrazione dei dati in archivio, ossia del sblocco dell’Utente. 406 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.13.4 Output Sblocco Utente. 3.2.14 Logout 3.2.14.1 Introduzione Questa funzionalità permette all’Amministratore di eseguire il logout dal sistema. 3.2.14.2 Input • Identificativo Amministratore (obbligatorio) 3.2.14.3 Elaborazione • Registrazione dei dati in archivio che tengono traccia del logout; 3.2.14.4 Output Dati relativi all’uscita dal sistema registrati in archivio : • Data ed Ora di logout Classe Utente 3.2.15 Login 3.2.15.1 Introduzione Questa funzionalità permette all’Utente di accedere al sistema. 3.2.15.2 Input • Username (obbligatorio) • Password (obbligatorio) 3.2.15.3 Elaborazione Visualizzazione di un form per l’input dei dati, sui quali il sistema esegue le seguenti operazioni : • Controlla che la Username e la Password siano corrette e cioè corrispondano a quelle memorizzate nell’archivio. Se il controllo va a buon fine, viene permesso l’accesso, altrimenti viene 407 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ concesso un altro tentativo ed in caso di un ulteriore errore, viene registrato l’accaduto e bloccato l’accesso al sistema; • Controlla che l’Utente non sia già loggato; • Registrazione dei dati relativi all’accesso; • Verifica dei diritti di accesso; • Visualizzazione del menù per l’accesso alle aree di utente; 3.2.15.4 Output Menù di accesso alle aree di utente. Dati relativi all’accesso registrati in archivio : • Data ed Ora di login • Indirizzo IP • Validità login • Tentativo Messaggi : “Username e/o Password errati” “Tentativi di accesso esauriti : impedito l’accesso” “Già loggato” 3.2.16 Registrazione 3.2.16.1 Introduzione Questa funzionalità permette all’Utente di effettuare la registrazione, per usufruire dei servizi offerti dalla web application. 3.2.16.2 Input Dati dell’Utente : • Email (obbligatorio) • Username (obbligatorio) • Password (obbligatorio) • Acquisto MP3 (si/no) 408 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.16.3 Elaborazione Visualizzazione di un form per l’input dei dati, sui quali il sistema esegue le seguenti operazioni : • Controlla che i campi relativi all’Email, Username, Password e non siano vuoti o non validi, in particolare l’Email abbia una struttura corretta e la Password abbia una lunghezza di almeno 5 caratteri; • Controlla che lo Username non sia già presente in archivio; • Registrazione dei dati di input in archivio; • Passaggio alla funzionalità di acquisto scheda prepagata qualora l’Utente voglia usufruire della funzionalità di download; 3.2.16.4 Output Tutti i dati letti in input registrati in archivio. Menù di accesso alle aree di utente. Altri dati registrati : • Data/Ora registrazione Messaggi : “Registrazione effettuata con successo” “Email non valida” “Il campo relativo all’email non può essere vuoto” “Il campo relativo alla Username non può essere vuoto” “Il campo relativo alla Password non può essere vuoto” “La Password deve essere almeno di 5 caratteri” 3.2.17 Acquisto scheda prepagata per il download di brani MP3 3.2.17.1 Introduzione Questa funzionalità permette all’Utente di acquistare una scheda prepagata “virtuale”, mediante la quale può eseguire l’acquisto e quindi il download degli MP3. 409 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.17.2 Input • Nome (obbligatorio) • Cognome (obbligatorio) • Indirizzo (obbligatorio) • Città (obbligatorio) • Provincia • CAP • Stato (obbligatorio) • Telefono • Data di Nascita • Sesso • Tipo Carta di Credito (obbligatorio) • Numero Carta di Credito (obbligatorio) • Data scadenza Carta di Credito (obbligatorio) • Codice Segreto (obbligatorio) • Importo (obbligatorio) 3.2.17.3 Elaborazione Visualizzazione di un form per l’input dei dati, sui quali il sistema esegue le seguenti operazioni : • Controlla che i campi relativi al Nome, Cognome, Indirizzo, Città e Stato non siano vuoti; • Controlla che sia stato selezionato un Tipo di Carta di Credito; • Controlla che il campo relativo al Numero Carta di Credito non siano vuoto o non valido; • Controlla che sia stata specificata la Data di Scadenza; • Controlla che il campo relativo al Codice Segreto della carta non sia vuoto; • Controlla che sia stato selezionato un Importo; • Controllo dei dati attraverso il sistema bancario; 410 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Registrazione dati relativi all’importo della scheda prepagata, registrati in archivio; 3.2.17.4 Output Alcuni dei dati di input registrati in archivio : • Importo Altri dati registrati in archivio • Data/Ora acquisto scheda prepagata • Data scadenza scheda prepagata Messaggi : “Il Controllo sul circuito bancario non ha avuto successo” “Scheda prepagata acquistata con successo” “Non è stata selezionata alcuna carta di credito” “Il campo relativo al numero carta di credito non può essere vuoto” “Il campo relativo al numero carta di credito ha un formato non valido” “Non è stata specificata la data di scandenza” “Il campo relativo al codice segreto non può essere vuoto” “Non è stato selezionato alcun importo per la scheda prepagata” “Il campo relativo al nome non può essere vuoto” “Il campo relativo al cognome non può essere vuoto” “Il campo relativo all’indirizzo non può essere vuoto” “Il campo relativo alla città non può essere vuoto” “Il campo relativo allo stato non può essere vuoto” 3.2.18 Ricarica scheda prepagata per il download di brani MP3 3.2.18.1 Introduzione Questa funzionalità permette all’Utente di ricaricare la scheda prepagata “virtuale”, mediante la quale può eseguire l’acquisto e quindi il download degli MP3. 411 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.18.2 Input • Identificativo utente (obbligatorio) • Tipo Carta di Credito (obbligatorio) • Numero Carta di Credito (obbligatorio) • Data scadenza Carta di Credito (obbligatorio) • Codice Segreto (obbligatorio) • Importo Ricarica (obbligatorio) 3.2.18.3 Elaborazione Visualizzazione di un form per l’input dei dati, sui quali il sistema esegue le seguenti operazioni : • Controlla che sia stato selezionato un Tipo di Carta di Credito; • Controlla che il campo relativo al Numero Carta di Credito non siano vuoto o non valido; • Controlla che sia stata specificata la Data di Scadenza; • Controlla che il campo relativo al Codice Segreto della carta non sia vuoto; • Controlla che sia stato selezionato un Importo di Ricarica; • Controllo dei dati attraverso il sistema bancario; • Registrazione dati relativi all’importo della ricarica, registrati in archivio; 3.2.18.4 Output Alcuni dei dati di input registrati in archivio : • Importo Ricarica Altri dati registrati in archivio • Data/Ora ricarica Messaggi : “Il Controllo sul circuito bancario non ha avuto successo” “Ricarica eseguita con successo” “Non è stata selezionata alcuna carta di credito” “Il campo relativo al numero carta di credito non può essere vuoto” 412 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ “Il campo relativo al numero carta di credito ha un formato non valido” “Non è stata specificata la data di scandenza” “Il campo relativo al codice segreto non può essere vuoto” “Non è stato selezionato alcun importo per la ricarica” 3.2.19 Visualizzazione importo residuo e scadenza scheda prepagata 3.2.19.1 Introduzione Questa funzionalità permette all’Utente di visualizzare l’importo residuo e la data di scadenza della scheda prepagata “virtuale”, mediante la quale può eseguire l’acquisto e quindi il download degli MP3. 3.2.19.2 Input • Identificativo Utente (obbligatorio) 3.2.19.3 Elaborazione • Controlla che la scheda non sia scaduta; • Caricamento dei dati presenti in archivio; • Visualizzazione dell’importo residuo e della data di scadenza; 3.2.19.4 Output Importo residuo e data di scadenza della scheda prepagata. Messaggi : “Attenzione ! La scheda è scaduta” 3.2.20 Modifica dati personali 3.2.20.1 Introduzione Questa funzionalità permette all’Utente di modificare i propri dati personali. 3.2.20.2 Input Dati dell’Utente : • Nome (obbligatorio) 413 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Cognome (obbligatorio) • Indirizzo (obbligatorio) • CAP • Stato (obbligatorio) • Provincia • Città (obbligatorio) • Telefono • Data di Nascita • Sesso • Email (obbligatorio) • Username (obbligatorio) • Password nuova (obbligatorio) • Conferma Password nuova (obbligatorio) 3.2.20.3 Elaborazione Visualizzazione di un form, contenente i dati attuali relativi all’Utente caricati dall’archivio, per l’input dei nuovi dati sui quali il sistema esegue le seguenti operazioni • Controlla che i campi relativi all’Email, Username, Password vecchia e nuova e Conferma Password non siano vuoti o non validi, in particolare l’Email abbia una struttura corretta e la Password abbia una lunghezza di almeno 5 caratteri; • Controlla che i campi relativi al Nome, Cognome, Indirizzo, Città e Stato non siano vuoti; • Controlla che la Password nuova e la Conferma Password coincidano, altrimenti visualizza un messaggio di errore; • Registrazione dei dati di input in archivio; 3.2.20.4 Output Tutti i dati letti in input registrati in archivio. Menù di accesso alle aree di utente. 414 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ Messaggi : “Aggiornamento effettuato con successo” “Email non valido” “Il campo relativo all’email non può essere vuoto” “Il campo relativo alla Username non può essere vuoto” “Il campo relativo alla Password nuova non può essere vuoto” “Il campo relativo alla Conferma Password non può essere vuoto” “La Password deve essere di almeno 5 caratteri” “la Password ed il relativo campo di Conferma non coincidono” “Il campo relativo al nome non può essere vuoto” “Il campo relativo al cognome non può essere vuoto” “Il campo relativo all’indirizzo non può essere vuoto” “Il campo relativo alla città non può essere vuoto” “Il campo relativo allo stato non può essere vuoto” 3.2.21 Visualizzazione elenco playlist 3.2.21.1 Introduzione Questa funzionalità permette all’Utente di visualizzare l’elenco delle playlist create in precedenza per poterle ascoltare. 3.2.21.2 Input Dati caricati dal sistema provenienti dall’archivio. 3.2.21.3 Elaborazione • Caricamento dei dati presenti in archivio; • Visualizzazione dell’elenco delle playlist; 3.2.21.4 Output Elenco delle playlist. Messaggi : “Nessuna playlist creata in precedenza” 415 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.22 Creazione di una playlist 3.2.22.1 Introduzione Questa funzionalità permette all’Utente di creare una nuova playlist. 3.2.22.2 Input • Nome Playlist (obbligatorio) 3.2.22.3 Elaborazione Visualizzazione di un form, per l’input dei dati sui quali il sistema esegue le seguenti operazioni • Controlla che il campo relativo al Nome Playlist non sia vuoto; • Registrazione dei dati di input in archivio; 3.2.22.4 Output Elenco delle playlist. Altri dati registrati in archivio : • Data/Ora creazione Messaggi : “Playlist creata con successo” “Il campo relativo al nome della playlist non può essere vuoto” 3.2.23 Cancellazione di una playlist 3.2.23.1 Introduzione Questa funzionalità permette all’Utente di cancellare uno o più playlist create in precedenza. 3.2.23.2 Input • Identificativo Playlist (obbligatorio) 3.2.23.3 Elaborazione Visualizzazione di un form, contenente l’elenco delle playlist, con possibilità di selezionare quali cancellare. Inoltre, il sistema effettua le seguenti operazioni : 416 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Controlla che sia stato selezionata almeno una playlist; • Eliminazione dei dati presenti in archivio relativi alla/e playlist selezionata/e; • Visualizzazione dell’elenco delle playlist; 3.2.23.4 Output Elenco delle playlist. Messaggi : “Eliminazione effettuata con successo” “Non è stata selezionata alcuna playlist” 3.2.24 Visualizzazione brani presenti nella playlist 3.2.24.1 Introduzione Questa funzionalità permette all’Utente di visualizzare l’elenco dei brani MP3 che compongono una particolare playlist. 3.2.24.2 Input • Identificativo Playlist (obbligatorio) Dati caricati dal sistema provenienti dall’archivio. 3.2.24.3 Elaborazione • Caricamento dei dati presenti in archivio; • Visualizzazione dell’elenco dei brani; 3.2.24.4 Output Elenco dei brani MP3 presenti all’interno della playlist. Messaggi : “La playlist è vuota 417 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.25 Ascolto della playlist 3.2.25.1 Introduzione Questa funzionalità permette all’Utente di ascoltare in streaming i brani MP3 che costituiscono una playlist. 3.2.25.2 Input • Identificativo Playlist (obbligatorio) 3.2.25.3 Elaborazione Visualizzazione di un form per l’avvio della riproduzione dei brani contenuti nella playlist. Inoltre, il sistema effettua le seguenti operazioni : • Controlla sia stata selezionata una playlist da ascoltare; • Caricamento del player MP3 per la ricezione in streaming e la riproduzione della playlist; 3.2.25.4 Output Messaggi : “Non è stata selezionata alcuna playlist da ascoltare” 3.2.26 Visualizzazione elenco brani MP3 per genere musicale 3.2.26.1 Introduzione Questa funzionalità permette all’Utente di visualizzare l’elenco dei brani MP3 presenti in archivio, relativamente ad un determinato genere musicale 3.2.26.2 Input • Genere Musicale (obbligatorio) 3.2.26.3 Elaborazione Visualizzazione di un form con l’elenco dei generi musicali disponibili, sulla base del quale, effettuando una scelta, il sistema effettua le seguenti operazioni : • Controlla che sia stato selezionato un Genere Musicale; 418 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Caricamento dei dati presenti in archivio; • Visualizzazione dell’elenco dei brani MP3 sulla base del genere musicale scelto; 3.2.26.4 Output Elenco dei brani musicali relativi al genere scelto Messaggi : “Nessun genere musicale selezionato” “Nessun brano disponibile in archivio per questo genere” 3.2.27 Ricerca di un brano MP3 secondo uno o più criteri 3.2.27.1 Introduzione Questa funzionalità permette all’Utente di cercare uno o più brani MP3 all’interno dell’archivio, sulla base di propri criteri, per poter effettuare su di essi delle eventuali operazioni, tra cui l’ascolto, il download e l’inserimento in una playlist. 3.2.27.2 Input • Titolo • Autore • Genere Musicale • Album 3.2.27.3 Elaborazione Visualizzazione di un form che permette all’Utente di stabilire i propri criteri di ricerca dei brani MP3. Inoltre, il sistema effettua le seguenti operazioni : • Controlla che almeno uno fra i campi Titolo, Autore, Genere Musicale e Album non sia vuoto; • Caricamento dei dati presenti in archivio; • Visualizzazione dell’elenco dei brani musicali; 419 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.27.4 Output Elenco dei brani musicali che corrispondono ai criteri impostati. Messaggi : “Nessun brano corrisponde ai criteri impostati” “Non è stato impostato alcun criterio di ricerca” 3.2.28 Visualizzazione informazioni di un brano MP3 3.2.28.1 Introduzione Questa funzionalità permette all’Utente di visualizzare le informazioni dettagliate di un brano MP3 presente in archivio. 3.2.28.2 Input • Identificativo brano MP3 (obbligatorio) Dati caricati dal sistema provenienti dall’archivio. 3.2.28.3 Elaborazione • Caricamento dei dati presenti in archivio; • Visualizzazione delle informazioni dettagliate del brano selezionato; 3.2.28.4 Output Informazioni dettagliate del brano selezionato. 3.2.29 Ascolto di un brano MP3 3.2.29.1 Introduzione Questa funzionalità permette all’Utente di ascoltare in streaming un brano MP3. 3.2.29.2 Input • Identificativo brano MP3 (obbligatorio) 3.2.29.3 Elaborazione Visualizzazione di un form per l’avvio della riproduzione del brano MP3 selezionato. Inoltre, il sistema effettua le seguenti operazioni : 420 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Controlla che sia stato selezionato un brano MP3 da ascoltare; • Caricamento del player MP3 per la ricezione in streaming e la riproduzione dle brano; 3.2.29.4 Output Messaggi : “Non è stata selezionata alcun brano da ascoltare” 3.2.30 Inserimento di un brano MP3 all’interno di una playlist 3.2.30.1 Introduzione Questa funzionalità permette all’Utente di inserire un brano MP3 all’interno di una playlist. 3.2.30.2 Input • Identificativo brano MP3 (obbligatorio); • Nome Playlist (obbligatorio); 3.2.30.3 Elaborazione Visualizzazione di un form per l’input dei dati sui quali il sistema esegue le seguenti operazioni : • Controlla che sia stato selezionato un brano MP3 da aggiungere ad una playlist; • Controlla che sia stata selezionata la Playlist di destinazione nella quale inserire il brano; • Registrazione dei dati di input in archivio; 3.2.30.4 Output Tutti i dati letti in input registrati in archivio. Messaggi : “Inserimento brano effettuato con successo” “Non è stato selezionato alcun brano” “Non è stata selezionata la playlist di destinazione” 421 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.31 Cancellazione di un brano MP3 da una playlist 3.2.31.1 Introduzione Questa funzionalità permette all’Utente di cancellare uno o più brani MP3 dalla playlist selezionata. 3.2.31.2 Input • Identificativo brano MP3 (obbligatorio); • Identificativo playlist (obbligatorio); 3.2.31.3 Elaborazione Visualizzazione di un form per la selezione del/i brani da rimuovere. Il sistema, inoltre, effettua le seguenti operazioni : • Controlla che sia stato selezionato almeno un brano; • Eliminazione dei dati presenti in archivio relativi al/i brani selezionati; 3.2.31.4 Output Elenco dei brani presenti nella playlist. Messaggi : “Eliminazione effettuata con successo” “Non è stato selezionato alcun brano” 3.2.32 Acquisto/Download di un brano MP3 3.2.32.1 Introduzione Questa funzionalità permette all’Utente di acquistare ed effettuare il download di un brano MP3 presente in archivio. 3.2.32.2 Input • Identificativo brano MP3 (obbligatorio) 422 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.2.32.3 Elaborazione • Il sistema controlla che l’importo residuo sia sufficiente all’acquisto del brano MP3. In caso negativo, avvia la funzionalità di acquisto di una scheda prepagata oppure di ricarica. • Caricamento dei dati dall’archivio relativi al brano selezionato; • Riduzione dell’importo residuo nella scheda prepagata in base al costo del brano; • Abilitazione al download; • Registrazione dei dati in archivio; 3.2.32.4 Output Tutti i dati letti in input registrati in archivio Altri dati registrati in archivio : • Data acquisto Messaggi : “Acquisto del brano eseguito con successo” “Importo insufficiente sulla scheda prepagata, prego ricaricare” “Nessuna carta prepagata a disposizione, effettuarne l’acquisto” 3.2.33 Richiesta username/password 3.2.33.1 Introduzione Questa funzionalità permette all’Utente di chiedere l’invio di una mail contenente la username e la password, qualora se ne fosse dimenticato. 3.2.33.2 Input • Email (obbligatorio) 3.2.33.3 Elaborazione Visualizzazione di un form per l’inserimento dei dati, sui quali il sistema effettua le seguenti operazioni : • Controlla che il campo relativo alla Email non sia vuoto e sia valido; 423 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ • Caricamento dei dati presenti in archivio; • Invio della mail contenente username e password; 3.2.33.4 Output Messaggi : “Mail inviata con successo” “Il campo relativo alla Email non può essere vuoto” “L’Email ha un formato non valido” “Email non registrata in archivio” 3.2.34 Logout 3.2.34.1 Introduzione Questa funzionalità permette all’Utente di eseguire il logout dal sistema. 3.2.34.2 Input • Identificativo utente (obbligatorio) 3.2.34.3 Elaborazione • Registrazione dei dati in archivio che tengono traccia del logout; 3.2.34.4 Output Dati relativi all’uscita dal sistema registrati in archivio : • Data ed Ora di uscita 3.3 Requisiti di prestazioni Nessuno. 3.4 Vincoli di progetto Nessuno. 424 Appendice A – SRS Web Application MP3-Web _________________________________________________________________ 3.5 Attributi 3.5.1 Sicurezza Come strumento di protezione è previsto l’utilizzo di una Password al fine di evitare accessi al sistema da parte di persone non autorizzate. 3.6 Altri requisiti 3.6.1 Database Il sistema software prevede l’utilizzo di un database relazionale per l’archiviazione dei dati. 425 Riferimenti Bibliografici _________________________________________________________________ Riferimenti Bibliografici Capitolo I N. Ford - “Art of Java Web Development” - Ed. Manning K. D. Mann - “JavaServer Faces in Action” - Ed. Manning C. Cavaness - “Programming Jakarta Struts 2nd Edition” - Ed. O’Reilly M. Pianciamore - “Principi di Ingegneria del Software – Design Pattern” - CEFRIEL G. Mecca - “Applicazioni Web J2EE : Java Servlet”, corso di Tecnologie di sviluppo per il Web - Università della Basilicata J. Falkner, K. Jones - “Servlets and JavaServer Pages : The J2EE technology Web tier” - Ed. Addison Wesley Capitolo II E. Armstrong, J. Ball, S. Bodoff, D.B. Carson, I. Evans, D. Green, K. Haase, E. Jendrock - “The J2EE 1.4 Tutorial” (cap. 17,18,19,20,21) - Sun Microsystems K. D. Mann - “JavaServer Faces in Action” - Ed. Manning Doris Chen Ph.D. - “JavaServer Faces and Sun Java Studio Creator : Experience the Next Generation Web-Application Framework”, Sun Tech Days 2005 - Sun Microsystems D. Goyal, V. Varma - “Introduction to JavaServer Faces (JSF)” - Sun Microsystems E. Burns - “About Faces : The JavaServer Faces API and how it relates to Struts” - Sun Microsystems 426 Riferimenti Bibliografici _________________________________________________________________ S. Shin - “J2EE Advanced” (www.javapassion.com) - Sun Microsystems M. Hall - Tutorial “JavaServer Faces 1.1” (www.coreservlets.com) A. Winder, C. Straub - “Extreme Reuse in JavaServer Faces Technology” , JavaOne Sun’s 2005 Worldwide Java Developer Conference - Oracle J. Pardi – “JavaServer Faces”, 25 Maggio 2005 Capitolo III Apache Software Foundation - “Official Struts documentation” Apache Software Foundation, C. Dumoulin - “Official Struts Tiles documentation” C. Cavaness - “Programming Jakarta Struts 2nd Edition” - Ed. O’Reilly J. Holmes - “Struts : The Complete Reference” - Ed. McGraw/Hill S. Spielman - “The Struts framework : Practical guide for Java programmers” - Ed. Morgan Kaufmann T. Husted, C. Dumoulin, G. Franciscus, D. Winterfeldt - “Struts in action : building Web applications with the leading Java framework” - Ed. Manning S. Shenoy - “Struts survival guide : basics to best practices” – Ed. ObjectSource M. Robinson, E. Finkelstein - “Jakarta Struts for dummies” - Ed. Wiley Publishing R. W. Barnes - “Introduction to Struts” - Project Refinery S. Shenoy - “Struts for J2EE Developers” (www.objectsource.com) 427 Riferimenti Bibliografici _________________________________________________________________ C. Cavaness - “Jakarta Struts 1.1 : ready for prime time”, Atlanta Java Users Group (AJUG) 2002 D. Lucek - “Basic Struts Web development” (www.lucek.com) Capitolo IV E. Burns - “About Faces : The JavaServer Faces API and how it relates to Struts” - Sun Microsystems C. McClanahan – “The Best of Both Worlds: Integrating JSF with Struts in Your J2EE Applications” (www.oracle.com) R. Barcia - “JavaServer Faces vs Struts : A brief comparison” (websphere.sys-con.com) C. McClanahan - “JavaServer Faces and Struts : Competition or Coexistence ?” SAML Security – “JavaServer Faces : A comparative study” (www.oneinfoplace.com) K.D. Mann – “Migrating from Struts to JavaServer Faces” - Virtua K. D. Mann - “From Struts to JavaServer Faces , Evolving your Web applications to support the new standard” (www.jsfcentral.com) C. McClanahan - “The evolution of Web application architectures”, O’Reilly Open Source Convention 2005 D. Geary - “JavaServer Faces : Finally, a component model for enterprise Java!”, Denver Java User’s Group 2003 428 Riferimenti Bibliografici _________________________________________________________________ Capitolo V R.S. Pressman - “Principi di Ingegneria del Software” - Ed. McGraw/Hill P. Atzeni, S. Ceri, S. Paraboschi, R. Torlone - “Basi di dati”, seconda edizione Ed. McGraw/Hill G. A. Di Lucca - “Corso di Ingegneria del Software”, slide a.a. 2003-2004 - Università degli Studi del Sannio Sybase - “Sybase Power Designer : Conceptual Data Model , User’s Guide” Sybase - “Sybase Power Designer : Object Oriented Model , User’s Guide” Sybase - “Sybase Power Designer : Physical Data Model , User’s Guide” Capitolo VI A. Cioroianu - “Upload Files with JSF and MyFaces” (www.onjava.com) B. Dudney - “Creating JSF Custom Components” (www.java.net) R. Hightower - “JSF for nonbelievers : JSF component development” (www.ibm.com) Apache Software Foundation - “Apache Jakarta Tomcat 5 official documentation : SSL Configuration How-to” S. Smirnov - “How to use Tiles with JSF” – Exadel Appendice A – SRS Web Application MP3-Web Software Engineering Standards Committee of the IEEE Computer Society – “IEEE Recommended Practice for Software Requirements Specifications”, IEEE Std 830-1993 429