Università degli Studi di Pisa Facoltà di Scienze Matematiche, Fisiche e Naturali Corso di Laurea Triennale in Informatica Utilizzo dei Framework “Java Server Faces” ed “Hibernate” per lo sviluppo di un servizio internet per i cittadini del Comune di Grosseto Tutore Aziendale Dott. Ludwig Bargagli Candidato Giulio Rossetti Tutore Accademico Prof. Vincenzo Gervasi Addı̀ 12 Ottobre 2007 2 Capitolo 1 Introduzione L’oggetto di questa relazione è il progetto di tirocinio svolto presso il Comune di Grosseto nel periodo che va dal 12/03/2007 al 25/07/2007. Il lavoro di progettazione, sviluppo e test del codice prodotto è stato eseguito negli uffici del SED (il centro servizi informatici del comune) sotto la supervisione del dott. Ludwig Bargagli. Durante il tirocinio ho realizzato un applicazione per la gestione degli oggetti smarriti sul territorio comunale che verrà utilizzata da parte della Polizia Municipale e dal Provveditorato. Tale applocazione nasce per rendere più agevole la raccolta dei dati riguardanti le denunce di smarrimento e ritrovo degli oggetti nel territorio provinciale: offre infatti uno sportello online tramite cui il cittadino può comunicare direttamente con gli impiegati della pubblica amministrazione per effettuare denunce di smarrimento e ricerche nel catalogo degli oggetti smarriti e ritrovati. Inoltre gli impiegati della Polizia Municipale e del Provveditorato possono, tramite un ulteriore interfaccia web loro dedicata, controllare lo stato delle denunce, consultare l’archivio delle stesse, e tenere traccia della dislocazione degli oggetti ritrovati nei depositi atti a tale scopo. Alcuni di questi servizi erano già in passato resi fruibili da un applicazione web realizzata in tempi brevi per la necessità di averla disponibile nell’immediato: per questo motivo il servizio fornito risultava incompleto e non esente da alcuni problemi di gestione e di lentezza nell’esecuzione in caso di un moderato sovrannumero di richieste contemporanee. Ho quindi riprogettato da zero una nuova versione dell’applicazione tenendo conto delle necessità degli utenti finali, cercando di renderla più intuitiva possibile per limitare le difficoltà di utilizzo, e allo stesso tempo migliorando la capacità del servizio stesso nei casi di richieste multiple. Altro motivo per 3 4 CAPITOLO 1. INTRODUZIONE cui si è resa necessaria la riscrittura dell’applicazione è che era stata realizzata con tecnologie che non ne consentivano più una facile manutenzione e aggiornamento, rendendola di fatto inutilizzabile per la gestione degli archivi ormai memorizzati nel database con tempi di accesso accettabili. Proprio per non perdere gli archivi già presenti, e poter in tal modo operare una sostituzione a caldo del precedente servizio, ho mantenuto la struttura delle tabelle del database: questa scelta ha comportato delle limitazioni in fase di progettazione e creato la necessità di valutare varie soluzioni per la gestione degli accessi al database. Tali alternative sono riportate nella sezione IV di questa relazione. Il servizio web realizzato consta quindi di due parti: un interfaccia di amministrazione e una per la fruizione del servizio da parte del cittadino. Il deployment dell’applicazione è stato effettuato su una macchina avente come sistema operativo Linux Ubuntu Server e Apache Tomcat come Servlet/JSP container. Tale macchina risiede all’interno della rete aziendale e per tale motivo è stato necessario configurare una macchina posta in una DMZ come reverse proxy (Apache http server) per rendere il servizio fruibile su internet. 1.1 Organizzazione della relazione La relazione qui presentata è strutturata in modo da dare una visione d’insieme dell’applicazione realizzata. Per mostrare con maggiore chiarezza le parti principali del servizio verranno presentate nel seguito di questa introduzione le tecnologie utilizzate, si passerà poi ad analizzare la parte riguardante l’interfaccia grafica (sezione I), la logica di backend (sezione II), la struttura del database (sezione III), i casi di studio relativi alle varie versioni dell’applicazione realizzate (sezione IV) ed una postfazione contenente le considerazioni sul tirocinio svolto e la bibliografia essenziale (sezione V). Ogni sezione tecnica è introdotta da un paragrafo di descrizione della parte dell’applicazione presa in esame e si completa con la discussione delle metodologie di implementazione focalizzando l’attenzione sui problemi incontrati durante lo sviluppo e sulle soluzioni proposte. Inoltre nei casi in cui ho dovuto operare delle scelte tra varie tecnologie propostemi o da me presentate ho presentato le motivazioni che mi hanno indotto a discriminare tra le varie alternative. Capitolo 2 Requisiti del progetto 2.1 Dettagli precedente versione del servizio Realizzata nel 2003, la precedente versione del servizio era implementata in Java (J2EE 1.3) secondo il paradigma JSP Model 1 e implementava le chiamate al database (Oracle) tramite l’ausilio di driver jdbc. Tale implementazione prevedeva l’uso per la parte di View di pagine JSP (Java server pages) e per la parte relativa al Controller di Servlet. La sua limitata manutenibilità è dovuta al pesante uso di scriptlet e procedure Javascript nelle pagine JSP che rendono, in molti casi, poco leggibile i sorgenti realizzati e inoltre quasi del tutto assente la separazione della logica applicativa da quella di presentazione. Un inconveniente di questa versione è senza dubbio la scarsa possibilità di riuso del codice che mi ha costretto ad una fase di reverse engineering prima di poter iniziare a strutturare la nuova versione. 2.2 Nuova implementazione I requisiti tecnici che sono stati richiesti esplicitamente per la realizzazione del progetto sono i seguenti: • Implementazione del pattern MVC (JSP Model 2) • Uso del Framework “Java Server Faces” (nella implementazione MyFaces fornita da Apache) • Gestione della stampa dei report in PDF • Gestione delle informazioni riguardanti le inserzioni tramite e-mail 5 6 CAPITOLO 2. REQUISITI DEL PROGETTO • Integrazione con sistema di autenticazione di tipo “Single Sign On” • Portabilità su più modelli di database • Studio di fattibilità per il possibile impiego del Framework “Hibernate” Inoltre sono state aggiunte, durante la fase di sviluppo, altre modifiche che hanno portato alla realizzazione di quattro differenti versioni del progetto: • La versione attualmente in uso (che soddisfa i requisiti richiesti) • Una versione facente uso di EJB3 • Una versione in cui la parte di business logic è realizzata (per alcuni servizi) tramite l’uso del Framework Hibernate • Una versione in cui la parte di business logic è realizzata (per alcuni servizi) tramite l’uso del Framework iBatis Le ultime tre versioni sono state realizzate solo come caso di studio per verificare la possibilità di introdurre nel progetto finale le tecnologie di volta in volta analizzate. In seguito verrà motivata la decisione per cui non sono state introdotte nella versione definitiva. Inoltre la versione definitiva dell’applicazione è stata resa disponibile, oltre che come applicazione web monolitica, anche come portlet per il CMS (Content Management System) Jetspeed2 distribuito da Apache. La procedura è stata scritta per integrarsi con il sistema di autenticazione (Single Sign On) dell’ente che permette l’accesso ai servizi tramite l’uso di un unico identificativo e di un’unica password oppure tramite l’uso di smart card come la carta di identità elettronica (cie) o carta nazionale dei servizi (cns). Per lo sviluppo ho fatto uso principalmente di strumenti Open Source e di Software Libero. L’ambiente di sviluppo utilizzato è stato Eclipse (ver. 3.2.2), con installato il plugin MyEclipse (ver. 5.1.1 GA) per lo sviluppo di applicazioni enterprise. Altre librerie utilizzate sono Tomahawk (package aggiuntivo di componenti per MyFaces). La fase di deployment è stata effettuata sul servlet container Apache Tomcat (ver. 5.5.23). Il progetto è stato sviluppato su Linux (inizialmente su Fedora Core 6 e successivamente su Ubuntu Feisty 7.04). 2.2. NUOVA IMPLEMENTAZIONE 7 Nel prossimo capitolo illustrerò le varie tecnologie utilizzate per la realizzazione del servizio (ed il principale pattern di programmazione seguito), in seguito entrerò nello specifico proponendo i dettagli implementativi del servizio realizzato. 8 CAPITOLO 2. REQUISITI DEL PROGETTO Capitolo 3 Tecnologie 3.1 Pattern MVC Model-View-Controller è il nome di un design pattern di fondamentale importanza per la realizzazione di applicazioni con interfacce grafiche nei linguaggi Object-Oriented (OO). Tale pattern obbliga il programmatore a scindere in modo netto i livelli fondamentali dell’applicazione, i componenti software che implementano il modello delle funzionalità business (model), quelli che si preoccupano di realizzare la logica di presentazione (view) e i componenti che utilizzano tali funzionalità (controller). Il sempre più diffuso uso di questo pattern ha fatto si che negli ultimi anni fiorissero numerosi framework, soprattutto per il web, atti a fornire agli sviluppatori la possibilità di realizzare applicazioni sempre più aderenti a tale modello con una sempre maggiore facilità e flessibilità d’uso. Alcuni dei più famosi framework che applicano tale pattern, attualmente disponibili per Java enterprise sono Java Server Faces, Struts e Velocity. Per rendere gestibile in modo efficiente lo sviluppo e la manutenzione del codice è quindi stato deciso di seguire un approccio basato sulla strutturazione del progetto secondo il pattern MVC. La parte di View e di Controller, conformemente al JSP model 2, è stata quindi realizzata tramite Java Server Faces (nell’implementazione MyFaces di Apache) mentre il Model tramite JavaBeans. 3.2 Java Server Faces Il framework utilizzato ai fini della realizzazione della logica di presentazione è stato JSF. JSF applica sistematicamente il pattern MVC, rappresenta inoltre uno 9 10 CAPITOLO 3. TECNOLOGIE standard gestito da Sun (codice JSR 172). Di questo framework esistono varie implementazioni: oltre a quella della Sun le principali sono quelle di Oracle e Apache. Le Gui realizzate in JSF sono configurabili tramite un file XML (faces-config.xml) in cui vengono definite le pagine facenti parte della view (pagine JSP facenti uso esclusivamente di particolari taglibrary), le regole di navigazione tra di esse e i bean utilizzati per implementare la parte di controller. Ogni implementazione usa una servlet di base FacesServlet o un filtro il cui mapping è solitamente /faces/* o *.faces.1 I principali vantaggi nell’uso di questo framework risultano essere: • Esistenza di componenti predefiniti che avvicinano la programmazione web a quella degli ambienti RAD, consentendo allo sviluppatore di realizzare in breve tempo interfacce web con la stessa semplicità offerta da ambienti come .NET, semplicemente “collegando” elementi di business logic lato server tramite catene di eventi. • Elementi GUI “intelligenti” in grado di validare in prima persona i dati inseriti dall’utente e di archiviare e caricare on-demand il proprio stato da bean memorizzati lato server denominati “model-object” . • Definizione di un nuovo paradigma di Event Handling che avvicina la programmazione in ambito web alla tipologia di gestione asincrona degli eventi utilizzata nelle applicazioni client-server. • Indipendenza dal markup language: ogni modello di interazione lato server viene realizzato lato client tramite Renderer diversificati che producono un interfaccia utente in grado di soddisfare i requisiti funzionali del server al meglio delle possibilità della piattaforma utilizzata dall’utente. 3.3 JasperReport Del progetto fa parte anche la gestione della stampa dei verbali delle denunce di smarrimento e ritrovo degli oggetti: per gestire la creazione di report in formato pdf ho utilizzato le librerie messe a disposizione da JasperReport poiché essendo rilasciate sotto licenza GPL sono liberamente utilizzabili e distribuibili. 1 In JSF le pagine JSP vengono, per convenzione, refenziate con estenzione .faces sul server. Tale convenzione di naming è introdotta per mostrare una separazione tra il precedente JSP Model 1 e il nuovo Framework pur mantenendo come base la stessa struttura di visualizzazione (entrambi utilizzano pagine JSP). 3.4. DATABASE 11 JasperReport è una libreria scritta in Java che permette di creare dei report in maniera semplice e di automatizzarne il popolamento. Il procedimento per la creazione del report, reso intuitivo dal tool iReport che consente di definire la struttura del documento in modo visuale, consta di poche semplici fasi: 1. Creazione della struttura del documento; 2. Definizione del DataSource da cui prelevare le informazioni per popolare il report; 3. Scrittura del codice necessario ad associare DataSorurce e struttura del documento. A seguito della prima fase verrà creato un file di tipo .jxrml che definisce, in xml appunto, la struttura definita in modo visuale. Tale file (che abbiamo deciso di salvare nella cartella WEB-INF/conf) contiene le associazioni necessarie per il popolamento del documento. I DataSource definibili sono di svariato tipo (è possibile, tra varie alternative, passare connessioni jdbc alle librerie jasperReport e associare al documento da popolare una query SQL), nel nostro caso abbiamo optato per un DataSource popolato dai campi di un Bean preesistente. 3.4 Database La precedente versione dell’applicazione sfruttava come DBMS Oracle 9; per rendere possibile la distribuzione anche su DBMS OpenSource abbiamo strutturato la nuova versione dell’applicazione in modo da garantire la compatibilità con MySQL e Postgres. Le tabelle hanno mantenuto la struttura della versione presente su Oracle per garantire la compatibilità con le pratiche precedenti alla nuova implementazione. E’ stata aggiunta una sola tabella per motivi che renderò noti nella sezione dedicata all’analisi dello schema del database. 3.5 Log4J Sempre per rendere più facilmente gestibili future modifiche al codice tutto il progetto ho implementato funzionalità di logging tramite le librerie log4j fornite da Apache. In seguito verranno mostrati esempi di uso. Ho introdotto il logger non solo per rendere più rapida la fase di debug del progetto durante 12 CAPITOLO 3. TECNOLOGIE lo sviluppo ma anche per rendere la manutenzione (e le future modifiche) del servizio gestibile a chi non ha partecipato al suo sviluppo. 3.6 EJB3 A seguito della conclusione del progetto abbiamo valutato la possibilità di introdurre l’uso di Enterprise Java Beans all’interno del Model ed ho quindi realizzato una versione dello stesso in cui è utilizzata questa tecnologia: tale implementazione non verrà usata ed è stata introdotta solo per valutarne l’usabilità. Le specifiche EJB intendono fornire una metodologia standard per implementare la logica di funzionamento delle applicazioni di tipo enterprise, applicazioni cioe’ che forniscono servizi via Internet. Esistono tre tipi di EJB: Entity, Session e Message Bean. • Gli Entity hanno come fine la memorizzazione delle istanze degli oggetti sul server. Tali EJB di entità forniscono quindi la caratteristica della persistenza dei dati. • I Session Bean gestiscono l’elaborazione delle informazioni sul server. Generalmente sono una interfaccia tra i client e i servizi offerti dai componenti disponibili sul server. • I Message Bean sono gli unici con funzionamento asincrono. Tramite il Java Message Service (JMS), si iscrivono a un argomento o a una coda e si attivano alla ricezione di un messaggio inviato all’argomento o alla coda a cui sono iscritti. Non richiedono una istanziazione da parte dei client. Nella nostra applicazione abbiamo fatto uso di Session Bean, nello specifico degli Stateless Session Bean, per gestire le richieste al database da parte delle varie entità in gioco. Come specificato dal nome gli Stateless Session Bean non tengono traccia, a seguito della invocazione di un loro metodo, dello stato dell’oggetto sul server; per ottenere una persistenza simile sarebbe stato invece necessario l’impiego di Statefull Session Bean. Per permettere l’uso di questa tecnologia è stato necessario configurare un application Server: i dettagli relativi alla scelta dell’application server usato verranno trattati nella sezione dedicata alla implementazione del progetto contenente EJB3. 3.7. HIBERNATE 3.7 13 Hibernate Hibernate è un middleware open source per lo sviluppo di applicazioni Java che fornisce un supporto di tipo ORM (object relational mapping); il suo compito è gestire la rappresentazione e il mantenimento su database relazionale di un sistema a oggetti Java. Distribuito sotto licenza LGPL, fornisce allo sviluppatore un mapping delle classi Java sulle tabelle del database e, sulla base di tale mapping, gestisce il salvataggio degli oggetti sul database. Inoltre si occupa del reperimento degli oggetti dal database, eseguendo automaticamente le query SQL necessarie a ottenere gli stessi e occupandosi del successivo instanzizione delle entità ottenute. L’obbiettivo di questo middleware è esonerare il programmatore dal lavoro inerente la gestione della persistenza dei dati. Le strade per utilizzare Hibernate in un progetto sono due: 1. è possibile, contestualmente alla fase di progettazione, definire l’uso di tale middleware nel progetto, in modo da strutturare il database nel modo ottimale per una gestione ad oggetti; 2. è altresı̀possibile introdurre Hibernate in un progetto già in fase di sviluppo, il cui database non sia stato necessariamente studiato per una sua applicazione, effettuando un reverse engeneering delle tabelle su cui si decide di introdurre l’uso del middleware. Hibernate è tipicamente usato sia in applicazioni Swing che J2EE facenti uso di servlet o EJB session beans. In un approccio MVC rientra nella parte dell’applicazione dedicata al Model. A causa della decisione di non modificare in modo pesante la struttura del database preesistente, presa a seguito dell’analisi dei requisiti, l’introduzione di questo framework per la persistenza dei dati è stata valutata solo al termine della messa in opera dell’applicazione. 3.8 iBatis Un approccio ulteriore per la gestione del Model, alternativo a quello offerto da Hibernate, è quello proposto da iBatis. Questo Framework non rientra nella categoria degli ORM, il suo fine è di rendere più facilmente manutenibile un applicazione che faccia uso di query SQL. Tramite l’uso di alcuni file xml di configurazione, infatti, tale framework consente di disaccoppiare la logica applicativa dalle query al database facendo 14 CAPITOLO 3. TECNOLOGIE in modo da rendere una modifica relativa all’SQL prodotto non influente, in modo strutturale, sul codice che ne fa uso. iBatis, inoltre, toglie al programmatore l’onere della gestione delle connessioni al database rendendo possibile configurare nel dettaglio in un ulteriore file xml i dettagli con cui queste debbano avvenire. Parte I La View 15 Capitolo 4 L’interfaccia utente rivolta al cittadino 4.1 Analisi dei Requisiti Il servizio offerto ai cittadini ha come fine ultimo facilitare la presentazione delle denunce di smarrimento e la ricerca nel database delle pratiche avviate (sia per lo smarrimento che per il ritrovo degli oggetti). Considerando come base il servizio precedentemente esistente, ho quindi cercato di diminuire il numero di interazioni necessarie al cittadino per ottenere le informazioni ricercate. Per la parte di compilazione della denuncia di smarrimento è adesso necessario riempire un unico form con i propri dati mentre per effettuare la ricerca, dopo averne impostato i parametri, scegliere da una lista di possibili risultati quello da visualizzare. Rendendo minimali le procedure, si è quindi provato a spostare l’operazione di dennucia di smarrimento dal classico servizio di ufficio, offerto dalla Pubblica Aministrazione, ad un servizio più snello e veloce affidato esclusivamente al cittadino. 17 18CAPITOLO 4. L’INTERFACCIA UTENTE RIVOLTA AL CITTADINO 4.2 Descrizione L’interfaccia pubblica del servizio consente la ricerca, da parte del cittadino, all’interno del database degli oggetti che sono stati ritrovati nel territorio comunale. Precedentemente tale funzione prevedeva una ricerca vincolata a: • Comune di smarrimento • Categoria dell’oggetto smarrito • Sottocategoria • Data di inserimento nel database • Data di ritrovamento • Tipologia di oggetto (Smarrito/Trovato) Per rendere più elastica la ricerca nella nuova versione ho sostituito archi temporali alle ricerche su singola data. Inoltre per evitare immissioni errate delle date stesse ho previsto per ogni campo data l’inserzione tramite un calendario a popup. I campi riguardanti il Comune, le Categorie e le Sottocategorie prevedono una selezione da apposito menu drop down popolato al caricamento della pagina tramite tre diverse chiamate al database. I risultati della ricerca sono visualizzati in una tabella dinamica dalla quale è possibile raggiungere per ogni oggetto visualizzato una scheda dettagliata. Altra funzione resa disponibile dall’interfaccia pubblica riguarda l’inserimento da parte dell’utente di segnalazioni di smarrimento di oggetti. Tali inserzioni nel database non vengono considerate attendibili fintanto che non sono validate da un operatore preposto allo scopo e quindi non compaiono subito come risultati di un eventuale ricerca. 4.2. DESCRIZIONE 19 20CAPITOLO 4. L’INTERFACCIA UTENTE RIVOLTA AL CITTADINO 4.3 Dettagli implementativi form di Ricerca La form di ricerca presenta tre selectOneMenu dedicati alla selezione del comune di interesse, della categoria e della sottocategoria dell’oggetto da ricercare, quattro inputCalendar per consentire la ricerca in base all’arco temporale di inserimento nel database e a quello di ritrovamento, due selectBooleanCheckbox per definire la tipologia di oggetti da ricercare (Ritrovati,Smarriti o entrambi) ed un commandButton per eseguire il submit e la navigazione.1 La pagina di visualizzazione risultati si compone di una dataTable che visualizza l’immagine (se presente), la descrizione, il luogo e la data di ritrovo di ciascun oggetto che soddisfi i criteri di ricerca. 1 I componenti elencati verranno esaminati nel dettaglio nella sezione successiva 4.3. DETTAGLI IMPLEMENTATIVI FORM DI RICERCA 21 Per comodità la capienza massima della tabella per pagina è fissata a 15 elementi, ciò ha comportato l’uso di un componente dataScroller per la generazione automatica di un indice di pagina riferibile alla tabella. Selezionando uno dei risultati si ottiene una scheda con i dettagli relativi all’oggetto. 22CAPITOLO 4. L’INTERFACCIA UTENTE RIVOLTA AL CITTADINO 4.4 Dettagli dei componenti utilizzati nel progetto • selectOneMenu: realizza un drop-down menu i cui valori sono caricati tramite il campo value, del componente figlio selectItems, collegato ad un metodo del bean che ha come valore di ritorno un ArrayList. Il valore scelto viene inserito nel bean tramite il campo value del componente. Nel caso del menu riguardante le categorie è stato inserito un campo valueChangeListener che consente di modificare il contenuto del menu delle sottocategorie nel momento in cui viene effettuato il refresh della pagina ad opera del commandButton agente sul metodo specificato in tale campo. • inputCalendar: questo componente sostituisce le l’imputText che precedentemente si occupavano di consentire all’utente l’immissione delle date di ricerca. Tale componente non necessita di controlli sui campi immessi (a differenza della precedente versione in cui tali controlli erano effettuati in Javascript) poiché valida ed invia solo le date scritte nel formato consentito (dd/MM/yyyy) e forza il refresh della pagina in caso contrario. E’ stato inoltre utilizzato il campo renderAsPopup per consentire la scelta della data desiderata tramite un calendario visualizzato a seguito della pressione di un commandButton disegnato dal componente. A causa del suo funzionamento il campo value di questo oggetto è utilizzato sia per accedere ai metodi di set che a quelli di get del bean usato, al momento del display infatti carica la data attuale tramite il get e al momento dell’invio la aggiorna a quella desiderata tramite il set. • selectBooleanCheckbox: effettua il display di una checkbox, il campo value effettua il set utilizzando un valore di tipo boolean. • commandButton: il pulsante “Cerca” è utilizzato per realizzare sia la funzione di navigazione sia quello di ricerca. Il pulsante Sottocategorie si occupa di fare il refresh della pagina e di riottenere il valore del nuovo menu delle sottocategorie tramite il metodo AggiornaCategoria sulla cui azione è in attesa il listener del componente selectOneMenu. Il pulsante “pulisci” si occupa solo di effettuare il refresh della pagina annullando tutte le scelte fatte precedentemente dall’utente: ciò è reso possibile dal campo immediate settato a true il quale specifica che i metodi di set chiamati dai componenti debbano essere valutati ed eseguiti solo a seguito dell’azione di navigazione. 4.5. PROBLEMI RISCONTRATI E SOLUZIONI PROPOSTE 23 • DataTable 1 : componente dedicato alla costruzione dinamica di tabelle, similmente a selectItems riceve un ArrayList di cui si preoccupa di fare il display. Tramite i tag column è possibile specificare il numero delle colonne e il loro contenuto nonchè il loro titolo. Il campo rows fissa il numero di righe massimo visibile in una singola pagina della tabella. • dataScroller: referenziando il componente dataTable si occupa del numero massimo di pagine su cui suddividere la tabella e di fornire dei link di navigazione tra le pagine stesse. • commandSortHeader: permette, tramite l’implementzione di alcuni metodi di cui viene effettuato il binding nella dataTable, di riordinare il contenuto della stessa relativamente alla colonna selezionata. • graphicImage: effettua la visualizzazione di un immagine. • inputFileUpload: consente il submit di file da form. Per abilitare tale componente è necessario configurare alcuni filtri nel file web.xml per definire la dimensione massima dei file invia • inputText: componente usato per inserire testo nei form. • outputText: componente usato per visualizzare il contenuto dei campi dei bean nelle schede dettagliate, nei risultati delle ricerche e nella sezione di gestione dell’applicazione. • message: componente usato per effettuare la visualizzazione di messaggi di errore in caso di fallimento nella validazione dei dati inseriti nel form. • inputHidden: componente usato per inserire valori nel form non visualizzati dall’utente. 4.5 Problemi riscontrati e Soluzioni proposte Durante le fasi iniziali dello sviluppo si sono presentati numerosi problemi non legati strettamente al codice prodotto bensı̀ all’ambiente di sviluppo (in particolare al plugin MyEclipse) che proponeva librerie di MyFaces ferme alla versione 1.1.1 e quindi solo in parte compatibili con le librerie di Tomahawk di 1 Successivamente all’uso nel progetto ho scritto un articolo su questo componente e sulla flessibilità che offre per riordinare dinamicamente, tramite semplici passaggi, il suo contenuto (Tale articolo è stato pubblicato su Dev, numero 152 Agosto 2007 [13]) 24CAPITOLO 4. L’INTERFACCIA UTENTE RIVOLTA AL CITTADINO versione 1.1.3 necessarie per l’uso di parte dei componenti dei vari form. Con l’aggiornamento di MyFaces alla versione 1.1.5 i problemi di compatibilità sono stati risolti2 . Alcuni dei principali problemi incontrati sono stati: 1. Impossibilità di effettuare correttamente il submit a seguito dell’inserzione dei componenti selectOneMenu all’interno del form. 2. Aggiornamento del selectOneMenu delle sottocategorie in modo da consentire la visualizzazione delle sole sottocategorie inerenti alla categoria selezionata (funzione precedentemente realizzata in Javasript). 3. Errato rendering dei componenti inputCalendar a seguito dell’impostazione del campo renderAsPopup a true. 4. Formattazione delle date per la ricerca all’interno del database. Per le problematiche precedentemente presentate ho applicato le seguenti modifiche alle librerie e/o ai file di configurazione: 1. Il problema era dovuto alla non corretta azione di set/get dei componenti in questione, per risolvere il problema ho aggiornato le librerie usate3 . 2. Ho realizzato l’aggiornameto prevedendo il reload della pagina tramite un comandButton apposito il cui metodo di riferimento viene monitorizzato dal campo valueChangeListener aggiunto nel componente selectOneMenu riguardante le categorie. Inoltre durante il primo caricamento della pagina viene effettuata una doppia interrogazione al database in modo da avere a disposizione subito le sottocategorie della prima categoria della lista. <h:selectOneMenu value="#{item.categoria} "valueChangeListener="#{categorieList. AggiornaCategoria}"> 2 A seguito di ricerche su forum di numerose comunità ho appreso che la retro compatibilità del package Tomahawk 1.1.3 (l’unica versione attualmente disponibile che rende correttamente utilizzabile inputCalendar e altri componenti in seguito utilizzati) è limitata alle versioni di MyFaces successive alla 1.1.2 a causa di una modifica nella struttura interna che, nelle precedenti relase, prevedeva l’inserimento di alcune classi di Tomahawk nel package di base. 3 Al termine della relazione si riporta per completezza la lista delle librerie utilizzate nel progetto (compresa versione) 4.5. PROBLEMI RISCONTRATI E SOLUZIONI PROPOSTE 25 <f:selectItems id="ls" value="#{categorieList.categorieItemList}"/> </h:selectOneMenu> <h:commandButton value="Sottocategorie" action="#{categorieList.AggiornaCategoria}"> </h:commandButton> <h:selectOneMenu value="#{item.sottocategoria}"> <f:selectItems value="#{categorieList. subCategorieItemList}"/> </h:selectOneMenu> 3. A seguito del persistere del problema, dopo l’aggiornamento delle librerie, un analisi del file web.xml di configurazione dell’applicazione ha mostrato l’errata compilazione dello stesso da parte dell’ambiente di sviluppo (l’errore è stato rintracciato grazie a interventi in merito sul sito del progetto Apache MyFaces, le impostazioni da inserire per il coretto funzionamento sono quelle di seguito riportate). <filter> <filter-name>MyFacesExtensionsFilter</filter-name> <filter-class>org.apache.myfaces.webapp. filter.ExtensionsFilter</filter-class> <init-param> <param-name>maxFileSize</param-name> <param-value>20m</param-value> </init-param> </filter> <filter-mapping> <filter-name>MyFacesExtensionsFilter</filter-name> <servlet-name>Faces Servlet</servlet-name> </filter-mapping> 4. Per effettuare la ricerca su archi temporali ho utilizzato un oggetto di tipo SimpleDateFormat per definire il template per la data coerente a quello preesistente. Inoltre ho appurato che il database Oracle permette in una semplice query SQL di confrontare un campo di tipo Date correttamente con una stringa che rappresenta una data, ma che in caso di ricerca 26CAPITOLO 4. L’INTERFACCIA UTENTE RIVOLTA AL CITTADINO in un arco temporale (utilizzando la clausola “between” ) è necessario effettuare una riconversione di tipo con il metodo to date(’Stringa Data’,’formato’)4 . 4 In 7.8 “Conversioni String-Date e Annotazioni” si vedranno le funzioni analoghe per MySQL e Postgres. 4.6. STRUTTURA 4.6 27 Struttura Nella seguente immagine si mostra l’albero di navigazione inerente alla parte dell’applicazione volta all’utenza pubblica: i rettangoli rappresentano le pagine facenti parte di questa sezione del progetto (identificate dal percorso scritto all’interno) mentre gli archi orientati mostrano le azioni di navigazione tra di esse. L’immagine viene realizzata analizzando il file di configurazione faces-config.xml in modo automatico da MyEclipse. 28CAPITOLO 4. L’INTERFACCIA UTENTE RIVOLTA AL CITTADINO Capitolo 5 L’interfaccia utente rivolta agli operatori 5.1 Analisi dei Requisiti Gli operatori necessitano di una procedura facilmente raggiungibile dai vari uffici preposti alla gestione delle denunce (Forze dell’ordine, gestione dei magazzini) che consenta, una volta dichiarate le proprie credenziali di accesso, di svolgere le attività di competenza. Per questo motivo è stata realizzata una interfaccia web che, similarmente a quella riservata ai cittadini, offre un servizio completo cercando di ridurre al minimo la complessità delle operazioni necessarie all’operatore. Per tale servizio si è previsto quindi due livelli di accesso: uno da amministratore, per i responsabili che possono personalizzare gli aspetti globali del servizio, ed uno da operatore che consente di avere accesso alle informazioni necessarie per la gestione delle denunce. Gli operatori sono in grado di: • Inserire denunce di smarrimento/ritrovo; • Effettuare ricerche; • Modificare e concludere pratiche. Gli amministratori possono: • Svolgere l’attività degli operatori; • Validare denunce pendenti; • Modificare le informazioni sui depositi disponibili; 29 30CAPITOLO 5. L’INTERFACCIA UTENTE RIVOLTA AGLI OPERATORI • Gestire gli avvisi per i responsabili dei depositi; • Modificare gli aspetti generali (Stemmi comunali e informazioni riguardanti il comune). Una clausola importante pervenuta al momento dell’analisi dei requisiti di questa parte del progetto, e non implementata nella precedete versione, riguarda la possibilità di ottenere una tracciabilità delle modifiche apportate ad ogni denuncia dagli operatori. Per ogni denuncia, infatti, deve essere possibile risalire sia all’operatore che ne ha convalidato l’inserzione sia a quello che ne ha, eventualmente, modificato lo stato in seguito. Per gestire la validazione delle denunce telematiche da parte dei cittadini inoltre è stato richiesto un servizio che avvisi i responsabili di tutte le dennunce pendenti. Per rendere questo servizio il più efficace possibile tali informazioni devono essere mostrate nella schermata principale dell’applicazione web e devono essere comunicate ai responsabili dei magazzini tramite un email contenente (se possibile) una foto dell’oggetto per una rapida ricerca tra i ritrovi in giacenza. Ultima importante richiesta è stata quella di rendere l’autenticazione alla procedura compatibile con quello presente per gli altri servizi offerti agli operatori in modo tale da effettuare un unico login per utilizzare più servizi. 5.2 Descrizione L’interfaccia privata dell’applicazione è rivolta agli operatori che hanno i diritti necessari alla modifica del database. L’autenticazione, gestita tramite la “BASIC autentication” del servlet/jsp container Tomcat, avviene tramite l’immissione del codice fiscale dell’operatore (tramite digitazione o tramite lettura da smartcard). Le funzionalità presentate dall’interfaccia privata riguardano i seguenti ambiti: • Inserzione denuncia smarrimento • Inserzione denuncia ritrovamento • Ricerca Oggetti presenti nel database • Validazione schede immesse dall’utente (tramite interfaccia pubblica) • Gestione di vari aspetti riguardanti la localizzazione del programma (foto default, logo del comune, e-mail per segnalazioni, depositi di giacenza degli oggetti ritrovati, gestione delle categorie). 5.3. DETTAGLI IMPLEMENTATIVI 31 Nel corso di questo capitolo affronterò i dettagli riguardanti autenticazione e funzionalità proposte. 5.3 5.3.1 Dettagli implementativi Inserzione denuncia smarrimento/ritrovamento Il form prevede campi per l’immissione dei dati del proprietario (ritrovante) dell’oggetto smarrito (ritrovato) e delle informazioni su data e area di smarrimento. E’ possibile inserire, se disponibili, un massimo di tre immagini dell’oggetto smarrito (tramite l’ausilio del componente inputFileUpload). Le informazioni sull’agente che inserisce l’oggetto sono salvate automaticamente all’interno della denuncia nel campo Operatore1 (essendo la prima immissione dell’oggetto all’interno del database). Contrariamente a quanto avviene 32CAPITOLO 5. L’INTERFACCIA UTENTE RIVOLTA AGLI OPERATORI nella parte pubblica la scheda della denuncia è visualizzabile subito dopo la compilazione ed immissione. I componenti JSF utilizzati per il form sono analoghi a quelli utilizzati nell’interfaccia pubblica per la denuncia di smarrimento, sono inoltre applicati tutti gli accorgimenti già motivati precedentemente per la gestione dei menu di selezione categorie/sottocategorie. 5.3.2 Ricerca oggetti presenti nel database La form di ricerca è analoga a quella presentata nella parte pubblica con la sola aggiunta di una booleanCheckBox che consente di filtrare (per la visualizzazione) le pratiche concluse. Questo filtro ha influenza sulla scheda dettagliata che è possibile selezionare dalla pagina di visualizzazione risultati (anche essa realizzata analogamente a quella della parte pubblica con una dataTable). Le schede dettagliate, infatti, sono di due tipi: • Modificabili: per le pratiche ancora non concluse • Definitive: per le pratiche concluse Le schede dettagliate per le pratiche non concluse consentono all’operatore di modificare, in ogni momento, qualsiasi dato della denuncia e/o di concludere la stessa registrando i dati mancanti (relativi al proprietario o al ritrovante). Inoltre da questa interfaccia è possibile impostare la visibilità della scheda. Trattandosi di una modifiche a entità già esistenti l’operatore viene memorizzato non nel campo Operatore1 bensı̀ in quello Operatore2 per consentire la tracciabilità dell’ultima modifica effettuata. Entrambe le schede dettagliate consentono di ottenere una stampa in pdf della denuncia di ritrovamento/smarrimento dell’oggetto, funzione di cui parlerò in seguito. 5.3.3 Validazione schede immesse dall’utente Nella pagina che presenta il menu principale del servizio è presente un avviso che riporta il numero delle denunce inserite dai cittadini, attraverso l’interfaccia pubblica, e che ancora non sono state controllate e validate da un operatore (quindi risultano invisibili ad una qualsiasi interrogazione al db da parte di utenti senza i necessari privilegi). Tramite un commandLink tale avviso conduce l’operatore ad una pagina in cui sono visualizzate le pratiche ancora non validate in una dataTable, essendo pratiche necessariamente non concluse è da qui possibile aprire la scheda dettagliata modificabile di ciascun oggetto per controllarne tutti i valori e impostarne la validità (e quindi la visibilità). 5.3. DETTAGLI IMPLEMENTATIVI 5.3.4 33 Interfaccia di Gestione Per accedere a questa sezione sono necessari privilegi ulteriori a quelli necessari all’accesso alla parte privata poiché ci si aspetta che non tutti gli amministratori possano gestire direttamente i dettagli personalizzabili del servizio. Anche in questo caso ho creato un apposito gruppo con i privilegi di accesso alla directory contenente le pagine di modifica. Il menu (presentato in figura) consente la possibilità di scegliere quale aspetto del programma da modificare. L’amministratore può modificare solamente i valori relativi al comune di competenza. Gestione Depositi 34CAPITOLO 5. L’INTERFACCIA UTENTE RIVOLTA AGLI OPERATORI La prima pagina presenta, in una dataTable, la lista completa delle informazioni relativa a ciascun deposito e la possibilità di inserire e/o eliminare nuovi depositi. Selezionando la descrizione del deposito di interesse sulla dataTable si apre una pagina di modifica del deposito stesso (in questo caso è necessaria l’apertura di una nuova pagina poiché ad essere modificati sono dei valori già presenti in una struttura dati salvata nella sessione e quindi non accedibili in modifica direttamente dalla pagina corrente senza prevedere un reload della stessa per garantire la consistenza dei valori visualizzati). Gestione Categorie Per rendere più malleabile la struttura di ricerca ho previsto la possibilità, per l’amministratore, di creare e eliminare categorie e sottocategorie. Ho posto particolare attenzione alla rimozione di queste entità per fare in modo da rendere consistente il contenuto della tabella degli oggetti nel database: non è quindi possibile rimuovere categorie che presentino sottocategorie (anche se vuote) e sottocategorie che contengano almeno un oggetto. 5.3. DETTAGLI IMPLEMENTATIVI 35 Gestione Immagini L’amministratore può inserire l’immagine da visualizzare nella tabella dei risultati di una ricerca in caso di assenza di un thumbnail dell’oggetto trovato. Altra immagine personalizzabile è quella riguardante lo stemma comunale che viene apposto come logo nell’angolo in basso a destra di tutte le immagini relative agli oggetti inseriti nel database1 . Gestione Mail Comunicazioni 1 Lo stemma non viene apposto ai thumbnail degli oggetti inseriti 36CAPITOLO 5. L’INTERFACCIA UTENTE RIVOLTA AGLI OPERATORI A seguito della compilazione di una denuncia nella parte pubblica del programma viene inviata una mail ai responsabili della validazione degli inserimenti nel database. In questa sezione è possibile definire gli indirizzi e-mail a cui far pervenire il messaggio. 5.4 Problemi riscontrati e soluzioni proposte A seguito della sostituzione del sistema di autenticazione dell’operatore (precedentemente realizzato tramite una form e quindi non conforme allo standard), e all’uso della basic autentication di Tomcat, a causa della strutturazione del framework JSF ho dovuto riscrivere parte della procedura di validazione dell’utente. Per scelta progettuale un utente può possedere privilegi da amministratore sui dati raccolti in più di un comune, pertanto, precedentemente avevo previsto nella form un campo per selezionare il comune su cui l’amministratore avrebbe svolto il suo lavoro per la sessione corrente. Con l’introduzione della basic autentication tale campo non era più presente al momento del login ed ho dovuto necessariamente fare in modo di renderlo disponibile (e soprattutto consistente) al caricamento della prima pagina a seguito dell’autenticazione effettuata con successo. Per effettuare il caricamento dei dati necessari al popolamento del drop-down menu dei comuni di competenza era necessaria una chiamata al database, effettuata tramite un apposito bean, che all’inizio di sessione però non risultava ancora inizializzato. Ho risolto tale problema inserendo, nella prima pagina ad essere caricata (quella della selezione del comune di competenza), prima della visualizzazione del drop-down menu il seguente componente: <t:inputHidden value="#{utente.nome}"></t:inputHidden> 5.4. PROBLEMI RISCONTRATI E SOLUZIONI PROPOSTE 37 sfruttando l’inizializzazione del bean, effettuata alla chiamata del metodo, ho inserito all’interno del costruttore del codice avente come side-effect il recupero dal FacesContext2 dell’identificativo dell’operatore (il codice fiscale) e, di maggiore importanza, l’inizializzazione della struttura dati contenente i comuni di competenza per l’utente tramite il metodo Validate() della stessa classe. ## Codice del costruttore public Utente(){ String user = FacesContext.getCurrentInstance(). getExternalContext().getRemoteUser(); this.setNome(user); this.Validate(); } ## Codice del metodo Validate() public void Validate(){ UtServDB ut = new UtServDB(); ArrayList<String> comuni = ut.ValidateUser(this); int l = comuni.size(); if(l==1) { ## Un solo Comune amministrabile this.setComcod(comuni.get(0)); Item it = new Item(); it.setComune(this.getComcod()); FacesContext.getCurrentInstance().getExternalContext(). getSessionMap().put("item", it); ArrayList<String> ls = new ArrayList<String>(); ls.add(comuni.get(0)); ComuneList cl = ut.getComDescr(ls); this.setComList(cl); }else{ ## Piu’ di un Comune amministrabile ComuneList cl = ut.getComDescr(comuni); 2 Il FacesContext è un oggetto reso disponibile da JSF che ingloba al suo interno la sessione e tutti i dati che in essa vengono salvati in modo trasparente al programmatore. JSF mira ad allontanare il programmatore dalla gestione manuale della sessione e degli oggetti Request e Response rendendo necessario solo di rado un intervento diretto su tali entità 38CAPITOLO 5. L’INTERFACCIA UTENTE RIVOLTA AGLI OPERATORI this.setComList(cl); } } Ho deciso di salvare in sessione il bean Utente per fare in modo che in qualsiasi momento fosse possibile recuperare tutte le informazioni relative all’operatore attualmente loggato. A causa dell’uso della “Basic autentication” non è possibile invalidare la sessione, ciò comporta l’impossibilità di effettuare un logout dall’applicazione in modo diretto, per ovviare si ha quindi la necessità di chiudere il browser per terminare in questo modo manualmente la sessione in corso. Si segnala inoltre che, di default, tutti gli altri bean definiti hanno vita limitata alla request, questo significa che ogni volta in cui è necessario recuperare dati il cui tempo di vita è ormai scaduto ne salvo precedentemente l’istanza nella sessionMap (ottenibile dal FacesContext) e da qui li recupero all’occorrenza. A causa del metodo di navigazione proposto da JSF non è stato possibile utilizzare un commandLink per raggiungere la prima pagina della cartella /Dep/ (cartella contenente le pagine relative alla gestione. Nel paragrafo successivo parlerò dei diritti di accesso definiti a livello di “Basic autentication” alle varie sezioni create): tale elemento di navigazione è stato sostituito con il seguente tag html standard: <a href={‘‘}..{‘‘}></a> Jsf, infatti, effettua la riscrittura della url solo a seguito dell’avvenuta navigazione e ciò comportava la possibilità di accesso alla sezione riservata /Dep/* anche ad utenti non autorizzati (era infatti possibile caricare la pagina /Dep/index.faces) anche se, una volta entrati nella prima pagina dell’area non risultava possibile effettuare modifiche ai dati poiché al successivo tentativo di navigazione la url era riscritta correttamente. Al caricamento della pagina relativa alla gestione dei depositi per mantenere consistenti i valori della lista presentata si è provveduto, come nel caso precedentemente esemplificato, a effettuare il caricamento tramite un side-effect del costruttore del Bean DepositiList. 5.5. AUTENTICAZIONE 5.5 39 Autenticazione 5.5.1 Tomcat Autentication Per sfruttare la Basic Autentication di Tomcat (sistema di autenticazione che automaticamente predispone, al tentativo di accesso dell’utente all’interno di un area protetta, una maschera di inserzione di username e password) sono state necessarie alcune modifiche ai file web.xml del progetto e tomcat-users.xml del servlet container. • tomcat-users.xml: All’interno di questo file sono stati aggiunti gli utenti, con relativa password e gruppi di appartenenza secondo una sintassi di questo tipo: <user username="nome_utente" password="pwd" roles="gruppo1,gruppo2,...,gruppoN"/> • web.xml: Qui sono state definite le policy effettive per l’accesso alle cartelle riservate alla parte privata dell’applicazione (/Edit/ e /Dep/) con la seguente sintassi: <security-constraint> <web-resource-collection> <web-resource-name>Area Riservata</web-resource-name> <url-pattern>/Edit/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> 40CAPITOLO 5. L’INTERFACCIA UTENTE RIVOLTA AGLI OPERATORI <security-constraint> <web-resource-collection> <web-resource-name>Area Riservata</web-resource-name> <url-pattern>/Dep/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>super</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>Richiesta Autenticazione</realm-name> </login-config> Come discusso precedentemente, e esplicato dall’XML riportato, sono stati creati due gruppi, admin riservato agli amministratori ordinari (che hanno il diritto di accesso alla sola cartella /Edit/), e super destinato ai superamministratori che hanno la possibilità di modificare la localizzazione del servizio e quindi di accedere anche alla cartelle /Dep/. La cartella /Pub/, non presente in alcuna regola di accesso, è l’unica navigabile dall’utente ordinario e presenta una interfaccia ridotta, rispetto a quella di amministrazione, per la denuncia di smarrimenti e per la ricerca tra gli oggetti presenti nel database. 5.5.2 Single Sign On Per introdurre il sistema di autenticazione di tipo “Single Sing On” abbiamo ricreato i gruppi specificati per la Basic Autentication di Tomcat sul Server Eldap del comune e inserito in tali gruppi gli utenti precedentemente registrati per il servizio. Per quanti riguarda il servizio di autenticazione tramite smart card questo si appoggia al sistema “Single Sing On” e pertanto non è stato necessario introdurre ulteriori modifiche all’applicazione. 5.6. STRUTTURA 5.6 Struttura Sezione Amministrazione Sezione Gestione 41 42CAPITOLO 5. L’INTERFACCIA UTENTE RIVOLTA AGLI OPERATORI Parte II Il Controller 43 Capitolo 6 Logica dell’applicazione 6.1 Analisi dei Requisiti La parte riguardante la logica applicativa deve essere strutturata in package ben definiti in modo da separare le varie componenti e le loro funzionalità, i nomi di tali package sono scelti secondo le convenzioni proposte dall’ente. Le procedure devono essere facilmente manutenibili ed aggiornabili, devono inoltre ridurre il carico di lavoro sul server in modo da consentire un accesso contemporaneo alle funzionalità offerte a più utenti (sia della Pubblica Amministrazione che ai cittadini). La strutturazione delle entità deve prevedere la possibilità di una successiva introduzione, sia a livello di Model che di Controller, di Framework per la persistenza dei dati e/o per il wrapping delle chiamate a database. 6.2 I package Nelle implementazioni realizzate il progetto prevede per la logica applicativa la seguente strutturazione in package: 1. it.grosseto.comune.bean : Bean e Basket Bean. 2. it.grosseto.comune.img : classi per la gestione delle immagini presenti nel database. 3. it.grosseto.comune.mail : classi per la gestione del servizio di invio mail 4. it.grosseto.comune.report : classi per la creazione dei report per la stampa delle denunce. 45 46 CAPITOLO 6. LOGICA DELL’APPLICAZIONE Mostro di seguito, separatamente, i package e le scelte progettuali. 6.3 Bean e Basket Bean Per mantenere, almeno in minima parte, il codice della precedente implementazione ho modellato le entità sulle tabelle preesistenti nel database, il risultato ottenuto è stato il seguente: Nome file: ItemArray.Java Descrizione: Questa classe definisce il bean di tipo “Item” , i metodi di set e get sono stati ricavati considerando i campi della tabella VUOSOGGETTI preesistente nel database. E’ usato per gestire gli oggetti nella creazione dei Report. Nome file: Item.Java (estende ItemArray) Descrizione: Sono stati aggiunti dei metodi per l’immissione delle date utilizzate nel form di ricerca per definire gli archi temporali di inserimento nel database e di ritrovamento. Implementa il set/get di tutti i tipi di dato (tra cui UploadFile) che non sono gestibili da iReport per la creazione dei pdf. E’ il bean usato per le ricerche, inserzioni e modifiche degli oggetti presenti nel database. Metodi di manipolazione del bean sono i seguenti: • public String cleanItem(): Utilizzato per ripulire il bean a seguito del refresh del form di ricerca e di immissione. • public String indexClean(): Utilizzato per ripulire i campi del bean a seguito della ricerca o dell’inserzione avvenuta con successo, necessario per consentire un corretto inserimento, non viziato da valori dei campi del bean “sporchi” . • public String Cerca(): Crea un oggetto di tipo ItemList e, dopo aver controllato che i valori della checkbox non siano posti entrambi a false, gli delega la ricerca degli oggetti che soddisfano i requisiti immessi nel form di ricerca e salvati nel bean. • public String CercaDaValidare():Carica nel bean i valori da visualizzare nella scheda dettagliata dell’oggetto. • public String Load(): Instanzia un oggetto di tipo UtServDB a cui delega il compito di effettuare il popolamento dei campi del bean con i valori ricavati dal database. E’ utilizzato per effettuare il display della scheda dettagliata. 6.3. BEAN E BASKET BEAN 47 • public String Aggiungi(): Instanzia un oggetto di tipo UtServDB a cui delega il compito di inserire nel database un oggetto con i valori dei campi dell’item, utilizzato nella form di immissione. • public String Visibile(): Setta il flag che indica se la pratica è visibile. • public String Termina(): Setta il flag che indica che la pratica è conclusa. • public String update(): Effettua la navigazione verso la scheda dettagliata dell’oggetto a seguito di un update dello stesso. • public String Smarrito(): Effettua la navigazione verso la scheda di inserzione delle denunce di smarrimento. • public String Ritrovato(): Effettua la navigazione verso la scheda di inserzione delle denunce di ritrovamento. • public void validateCodFis(FacesContext context, UIComponent toValidate, Object value): Valida la “plausibilità” del codice fiscale inserito. • public void validateEmail(FacesContext context, UIComponent toValidate, Object value): Valida la “plausibilità” della e-mail inserita. NB: tutti i metodi di manipolazione effettuano anche una azione di navigazione. Nome file: ItemList.Java Descrizione: Questa classe definisce un basket per i bean di tipo Item. Il costruttore istanzia una struttura dati di tipo ArrayList in cui vengono memorizzati oggetti di tipo Item ottenuti a seguito di interrogazioni al database. Metodi di manipolazione del basket bean: • public ArrayList getList(): restituisce la struttura dati utilizzata per memorizzare gli oggetti di tipo Item. • public void addItem(Item a): inserisce nel basket l’oggetto di tipo Item ricevuto come parametro. • public int getItemconv(): Ottiene il numero delle schede ancora non validate (pratiche non visibili). • public void Cerca(Item a): Instanzia un oggetto di tipo UtServDB a cui delega il compito di effettuare la ricerca secondo i valori ottenuti dai campi dell’oggetto di tipo Item passato. Il risultato dell’interrogazione viene salvato nel FacesContext. 48 CAPITOLO 6. LOGICA DELL’APPLICAZIONE • public ArrayList getListTable(): Recupera dal FacesContext la lista precedentemente salvata e la restituisce. • public ArrayList setListTable(): non implementato. Presenza necessaria per la corretta chiamata del relativo metodo get del bean da parte dell’interprete delle istruzioni JSF. • public void CercaDaValidare(String comune): Cerca schede degli Item da validare (non visibili). Nome File: ItemTableBean.Java Descrizione: Bean(riconosciuto in JSF come itemTable) usato per ordinare i campi della tabella item usata per il display dei risultati dei vari form di ricerca. Metodi di manipolazione del bean: • public ArrayList hItemi getItemInventory(): Restituisce l’ArrayList contenente la lista di item ordinata. • protected void sort(Item[] app): Ordina l’array di Item ricevuto come parametro ripetto alla colonna desiderata. • public void setSortColumnName(String sortColumnName): Seleziona la colonna su cui effettuare l’ordinamento. Nome File: Categoria.Java Descrizione: Definisce il bean di tipo Categoria. I metodi di set e get sono stati ricavati considerando i campi della tabella VUOSTIPI preesistente nel database. La memorizzazione al suo interno di categoria e relative sottocategorie è realizzata con un oggetto di tipo String per il nome della categoria e un oggetto di tipo ArrayListhStringi per i nomi delle sottocategorie. I metodi di set e get sono quelli necessari a popolare il bean e a restituirne i valori. Nome File: CategorieList.Java Descrizione: Questa classe definisce un basket per i bean di tipo Categoria. Il costruttore istanzia una struttura dati di tipo ArrayList in cui vengono memorizzati oggetti di tipo Categoria ottenuti a seguito di interrogazioni al database. Metodi di manipolazione del basket bean: • public void addCategoria(Categoria a): Inserisce L’oggetto di tipo Categoria nel basket. 6.3. BEAN E BASKET BEAN 49 • public ArrayListhCategoriai getAllCategorie(): Restituisce il contenuto del basket. • public int size(): Ritorna il numero di Categorie presenti nel basket. • public SelectItem[] getCategorieItemList(): Prepara un array di SelectItem contenente le categorie nel basket per popolare il relativo dropdown menù. I valori sono ottenuti tramite un interrogazione al database delegata ad un oggetto di tipo UtUserDB. • public SelectItem[] getSubCategorieItemList(): Prepara un array di SelectItem contenente le sottocategorie relative alla categoria richiesta per popolare il relativo drop-down menu con le sottocategorie relative alla categoria attualmente selezionata. I valori sono ottenuti tramite un interrogazione al database delegata ad un oggetto di tipo UtUserDB. La prima volta che il metodo viene chiamato assume come categoria corrente la prima restituita dal database. • public String AggiornaCategoria(ValueChangeEvent event): Cattura l’evento di pressione del commandButton per l’aggiornamento delle sottocategorie e fa in modo che avvenga l’aggiornamento del drop-down menu relativo. • public String AggiornaCategoria(): Metodo atteso dall’omonimo listener, implementa la navigazione. • public String getCategoria(): Restituisce il nome della categoria attualmente selezionata. • public String AdvSearchClean(): Esegue il clean della variabile che memorizza la categoria attualmente selezionata. Nome File: Comune.Java Descrizione: Definisce il bean di tipo Comune. I campi memorizzati sono: nome del comune (String) e codice del comune (String). I metodi di set e get sono stati ricavati considerando i campi della view VUOSCOMUNI preesistente nel database. Nome File: ComuneList.Java Descrizione: Questa classe definisce un basket per i bean di tipo Comune. Il costruttore istanzia una struttura dati di tipo ArrayList in cui vengono memorizzati oggetti di tipo Comune ottenuti a seguito di interrogazioni al database. Metodi di manipolazione del basket bean: 50 CAPITOLO 6. LOGICA DELL’APPLICAZIONE • public ArrayList getListComuni(): Restituisce la struttura dati in cui sono memorizzate le informazioni sui Comuni. • public SelectItem[] getComuneItemList(): Prepara un array di SelectItem contenente i comuni per popolare il relativo drop-down menù. I valori sono ottenuti tramite un interrogazione al database delegata ad un oggetto di tipo UtUserDB. • public void add(Comune app): inserisce nel basket l’oggetto di tipo Comune ricevuto come parametro. Nome File: Deposito.Java Descrizione: Definisce il bean di tipo Deposito. Di questa entità si tiene traccia del nome, del referente, dell’ indirizzo, del numero di telefono e del contatto email. I metodi di manipolazione del bean sono: • public String Save(): Salva il deposito nel database. • public String Delete(): Elimina il deposito nel database. • private void Clean(): Pulisce i campi del bean. • public String Edit(): Modifica il deposito nel database. • public String EditDep(): Effettua la navigazione verso la pagina per la modifica del deposito. Nome File: DepositiList.Java Descrizione: Questa classe definisce un basket per i bean di tipo Deposito. Il costruttore si preoccupa di popolare l’ArrayList contenente le informazioni dei depositi relativi al comune di interesse tramite un metodo della classe. I metodi di manipolazione del basket sono: • public void add(Deposito dep): Effettua l’inserzione di un oggetto di tipo deposito nel basket. • public ArrayListhDepositoi getDepositi(): Restituisce l’ArrayList contenente i Depositi. • public void setDepositi(ArrayListhDepositoi list): Setta la lista ricevuta come parametro come lista dei Depositi attuale. • public String Cerca(): Effettua la ricerca nel db dei depositi. 6.4. GESTIONE DELLE IMMAGINI 51 • public SelectItem[] getDepositiItemList(): Ottiene una lista dei depositi per il drop-down menù. Nome File: Utente.Java Descrizione: Definisce il bean di tipo Utente. Di questa entità sono interessanti il nome e il codice relativo al comune attualmente amministrabile. Il suo costruttore si procura il nome dell’utente dal FacesContext (a causa del Single Sign On esso è salvato nell’oggetto RemoteUser), e verifica quali comuni possono da questi essere amministrati. I metodi di manipolazione del bean sono: • public void Validate(): Valida l’utente e ricava la lista dei comuni amministrabili. • public SelectItem[] getComuneItemListUser(): Ottiene una lista dei comuni amministrabili dal’utente. • public String ItemComCod(): Setta all’interno dell’oggetto Item da inserire o modificare il codice comunale dell’operatore. 6.4 Gestione delle immagini La gestione delle immagini presenti nel database è stata realizzata tramite una classe di appoggio e tre servlet. La classe di appoggio gestisce la conversione tra vari formati delle immagini ed il ridimensionamento delle stesse mentre le servlet si occupano di effettuare la stampa a video asincronamente al caricamento della pagina JSF. L’uso delle servlet è stato adottato per rendere possibile in parte il riuso del codice precedente e per sopperire alla mancanza in JSF di un meccanismo nativo che offra la possibilità di caricamento asincrono delle immagini. Una gestione asincrona del caricamento delle immagini è necessaria in particolar modo quando deve essere gestita la visualizzazione dei risultati di una ricerca: in tal caso infatti sono necessarie più interrogazioni al database per ottenere le immagini di ciascun oggetto trovato e attendere che tutte le query siano state completate prima di visualizzare la pagina ritarderebbe sensibilmente il tempo di caricamento. Inoltre non esiste un componente JSF che possa essere utilizzato in tale ottica quindi la soluzione adottata mi è sembrata l’unica percorribile. Nome File: UtImage.Java Descrizione: Classe per la gestione delle immagini. Metodi di gestione: 52 CAPITOLO 6. LOGICA DELL’APPLICAZIONE • public Image resizeImg(ImageIcon imageIcon, String resized, int size, float factor): Metodo per cambiare le dimensioni ad un’immagine. • public byte[] getImageByteArray(Image image): Prende un immagine e restituisce un byte[] con dentro l’immagine. • public Image resizeImg(ImageIcon imageIcon, int size, float factor): Effettua il resize dell’immagine. • public Image resizeImgLogo(ImageIcon imageIcon, int size, float factor, String comcod): Effettua il resize per il logo. • private Image fetchLogo(String comcod): Estrae il logo dalla tabella degli stemmi. • public BufferedImage convert(Image im): Converte una Image in una BufferedImage. • public String insertStemma(): Inserisce lo stemma del comune nel database. • public BufferedImage convertImgToBimg(Image im): Converte una Image in una BufferedImage • public String insertImgVuoto(): Effettua l’inserzione dell’immagine usata per segnalare nei risultati gli oggetti senza foto. • private void clean(): Pulisce i campi del Bean. Nome File: GetImg.Java Descrizione: Servlet per recuperare le immagini dal db e inviarli alle JSF. Nome File: GetImgBig.Java Descrizione: Servlet per recuperare le immagini a grandezza naturale dal db e inviarli alle JSF. Nome File: GetImgThumb.Java Descrizione: Servlet per recuperare le immagini a grandezza ridotta dal db e inviarli alle JSF. 6.5. GESTIONE DELLE EMAIL DI SERVIZIO 6.5 53 Gestione delle email di servizio Per l’invio delle mail ho realizzato le seguenti classi: Nome File: Mail.Java Descrizione: Bean usato per comporre la mail da inviare, si occupa di creare la struttura in modo trasparente alla classe SendMail che effettua dell’inoltro. Inoltre è usata per gestire la tabella degli indirizzi di inoltro presente nella parte riservata ai super-amministratori nell’interfaccia privata dell’applicazione. Metodi implementati: • public void compose(String comune,String descr): Compone il testo dell’email. • public void addImage(UploadedFile img): Allega un immagine alla mail. • public void send(): Chiama il metodo di invio della classe SendMail. • public String insertmail(): Inserisce un nuovo indirizzo email nel database. • public String delete(): Cancella un indirizzo email dal database. • public void clean(): Pulisce i campi del Bean. Nome File: MailList.Java Descrizione: Basket Bean per Mail. Metodi implementati: • private void Cerca(): Effettua la ricerca degli indirizzi di inoltro per il comune corrente. • public ArrayListhM aili getMail(): Restituisce una struttura dati per la presentazione dei risultati della ricerca in forma tabellare. Nome File: SendMail.Java Descrizione: Classe che si occupa dell’invio delle email. Metodi implementati: • public void send(): Effettua l’invio della email. 54 CAPITOLO 6. LOGICA DELL’APPLICAZIONE 6.6 Gestione dei report pdf Nell’applicazione ho introdotto la possibilità di creare automaticamente, partendo dai dati registrati nel database, i pdf relativi alle denunce di smarrimento, a quelle di ritrovo e dei documenti che attestano la giacenza di un oggetto nel deposito. Nella versione precedente dell’applicazione tale funzionalità era implementata solo in parte e tramite librerie con licenza proprietaria, per tale motivo mi è stato richiesto di reimplementarla facendo uso di librerie e strumenti OpenSource. Per soddisfare questa necessità ho fatto uso delle librerie jasperReport e del tool di sviluppo iReport di cui ho brevemente parlato nella sezione riguardante le tecnologie utilizzate. Package usati: jasperreports-1.3.3.jar itext-2.0.2.jar Versione di iReport usata 1.3.3 Nome File: StampaRep.Java Descrizione: Servlet per recuperare i dati relativi al report e gestirne la stampa come pdf sul canale di out. Nome File: reportDataSource.Java Descrizione: Bean per la gestione e creazione dei Report tramite le librerie di jasperReport. Metodi implementati: • private void getReport(JasperReport jasperReport,MaphString, Stringi parameters,OutputStream out): Esegue la stampa del report sul canale di out. • public void DenunciaRitrovo(OutputStream out,String type): Recupera il file jrxml relativo al report e ne predispone la scrittura. 6.6.1 Creazione di un report Per chiarire meglio come avviene il processo di creazione e riempimento del report mostro qui di seguito il codice relativo: //Path assoluto della classe per Tomcat String AbsPath = reportDataSource.class.getResource(""). toURI().getPath(); //Definisco il path relativo dalla classe 6.6. GESTIONE DEI REPORT PDF 55 //corrente al file di properties String RelPath = "../../../../../conf/"+type+".jrxml"; //Concateno i due percorsi String Path = AbsPath+RelPath; String fileJrxml = Path; //Carico il file jrxml JasperDesign jasperDesign = JRXmlLoader.load(fileJrxml); JasperReport jasperReport = JasperCompileManager. compileReport(jasperDesign); //Ottengo l’item da inserire nel report Item it = new Item(); it.setComune(this.comcod); it.setProgre(new Integer(this.progre)); UtServDB ut = new UtServDB(); //Popolo l’item da inserire ItemArray ia = ut.searchDetail(it); ia.add(); try { //Setto il DataSource JRBeanArrayDataSource jrbean = new JRBeanArrayDataSource(ia.getArray()); //Creo il JasperPrint asperPrint jasperPrint = JasperFillManager. fillReport(jasperReport, parameters, jrbean); //Stampo il documento ottenuto come Pdf sul canale out JasperExportManager. exportReportToPdfStream(jasperPrint,out); } catch (JRException e) { e.printStackTrace(); } Come si può vedere all’inizio reperisco un file con estensione .jrxml. Tale file è stato creato precedentemente tramite iReport e rappresenta lo “scheletro” del report che successivamente andrò a popolare con i dati recuperati dal database e salvati in un array di Bean. Il file viene recuperato costruendo 56 CAPITOLO 6. LOGICA DELL’APPLICAZIONE il percorso relativo alla sua locazione in due fasi: innanzitutto ottengo, tramite il metodo apposito, il percorso assoluto sulla macchina della mia classe e quindi (per garantire la portabilità del codice) gli concateno il percorso relativo dalla classe al file .jrxml. Effettuata questa fase di “lookup” dello scheletro del report mi preoccupo quindi di riempirlo e di effettuarne la stampa su un outputstream ricevuto come parametro. Parte III Il Model: gestione Database e Log 57 Capitolo 7 Database 7.1 Analisi dei Requisiti Per evitare momentanee sospenzioni di servizio deve essere mantenuto il database esistente; l’applicazione deve essere in ogni caso portabile su più tipologie di database in modo da garantire all’ente il suo funzionamento anche in caso di cambio del DBMS. Le classi preposte all’interazione con il database devono essere facilmente modificabili per la possibile intruduzione successiva di Framework per la persistenza dei dati (Hibernate) e/o per il mapping delle query SQL (iBatis). 7.2 Premessa Originariamente il progetto è stato sviluppato per utilizzare un DBMS Oracle (versione 10.x.x); al termine del progetto, per rendere portabile l’applicazione su i più diffusi DBMS OpenSource (Postgres e MySQL), ho apportato delle modifiche al codice della classe che implementa l’interazione con i database. Tali modifiche sono state necessarie, principalmente, per consentire il corretto accesso ai tre database supportati, per risolvere alcune problematiche legate alle differenze nella gestione dei Blob da parte degli stessi e a causa delle differenti funzioni per la conversione tra String e Date. Per definire il database in uso ho introdotto un file di properties (WEB-INF/conf/database.properties) che riporterò in seguito per intero. Per quanto riguarda l’interazione con il database ho realizzato un package (it.grosseto.comune.db) il cui fine è quello di realizzare la parte riservata alle query SQL e alla gestione del log dell’applicazione. Inizialmente era stata valutata la possibilità di sostituire completamente le query SQL con un framework per la persistenza dei dati (sul modello di Hibernate) ma a 59 60 CAPITOLO 7. DATABASE causa sia della precedente struttura delle tabelle, sia della complessità di alcune query implementate è stato deciso di mantenere nella versione finale un approccio simile a quello della precedente versione dell’applicazione. Tutte le query preesistenti sono state riformulate e modificate in modo tale da poter essere eseguite sui tre DBMS che seppure supportino appieno il linguaggio SQL standard presentano in alcuni casi dei “dialetti” molto differenti tra loro. Su questo argomento ritornerò nella sezione apposita1 . 7.3 Implementazione Nome File: UtServDB.Java Descrizione: Questa classe si occupa di interfacciare i bean (e quindi JSF) con il database tramite chiamate in SQL. Elenco metodi implementati e descrizione: • public Connection connectDB (): Stabilisce una connessione con il database tramite i driver jdbc. • public ArrayList getComuni(): Effettua l’interrogazione necessaria ad ottenere i valori relativi ai comuni presenti nel database. Per ottimizzare il refresh delle pagine, evitando chiamate inutili al database, ho previsto un flag che indica se sono già presenti nel bean le informazioni richieste, in tal caso non viene effettuata la richiesta. • public ArrayList getCategorie(): Effettua l’interrogazione necessaria ad ottenere i valori relativi alle categorie e sottocategorie presenti nel database. • public ArrayList searchItems(Item a) : Effettua la ricerca degli oggetti nel database che soddisfano determinati campi settati nell’item ricevuto come parametro, è utilizzato per ottenere i risultati del form di ricerca. • public Item searchDetail(Item item): Effettua la ricerca degli oggetti nel database che soddisfano determinati campi settati nell’item ricevuto come parametro, è utilizzato per ottenere i dettagli dell’oggetto selezionato tra i risultati della ricerca. • public void insertItem(Item item): Effettua l’inserzione nel database dell’oggetto passato come parametro (i valori dei cui campi sono ricavati dalla form di inserzione). 1 Vedi “Sequence e Autoincrement” , “Gestione campi blob” e “Conversioni String-Date e annotazioni” 7.3. IMPLEMENTAZIONE 61 • public int getProgre(String comcod): Restituisce il numero progressivo ottenuto dalla sequence del comune desiderato. • public Deposito LoadDeposito(String comcod,String deprog): Carica le informazioni riguardanti il deposito relative all’oggetto desiderato. • private void insertImage(UploadedFile u,String comcod,String descr,int progre): Carica l’immagine ricevuta nel database, utilizza metodi di utilità dell classe UtImage. • public ArrayList ValidateUser(Utente utente): Controlla se l’utente è abilitato ad amministrare gli oggetti smarriti di uno o più comuni. • public ComuneList getComDescr(ArrayListhStringi comuni): Ottiene le informazioni relative alla descrizione del comune. • public void updateItem(Item item): Aggiorna il valore dell’oggetto nel db con quelli dell’Item passato come parametro. • public int countValidateItem(): Ritorna il numero delle schede degli oggetti immessi dagli utenti e non ancora validate. • public ArrayList searchItemsValidate(String comcod): Ricava le schede da convalidare del comune desiderato. • public ArrayListhDepositoi getDepositi(String comcod): Interroga il database per ottenere una lista dei depositi per il comune specificato. • public void saveDeposito(Deposito deposito): Salva il deposito ricevuto nel db. • public void deleteDeposito(Deposito deposito): Elimina dal database il deposito ricevuto in input. • public void editDeposito(Deposito deposito): Aggiorna i valori del deposito selezionato. • public String getDepDescr(String comcod,String deprog): Ottiene la descrizione del deposito. • public String getComNome(String cod): Ottiene il nome del comune relativo al codice ricevuto come parametro. • public void createCat(String cat): Crea una nuova Categoria. 62 CAPITOLO 7. DATABASE • public void addSubCat(String cat, String subcat): Crea una nuova sotto categoria. • public void removeSubCat(String categoria, String subcat): Rimuove una sotto categoria vuota • public void removeCat(String categoria): Elimina una categoria che non presenta sottocategorie. • public void insertIntoTableVUOSIMM(String comcod, int progre, byte[] bufferBig, byte[] bufferSmall, String descrizione): Inserisce l’immagine nel DB nella tabella VUOSIMMAGINI(sia l’immagine, il thumbnail che la descrizione nella posizione identificata da comcod e progre). • public String[] getId (String comcod, int progre): Ottiene gli identificativi delle immagini relativi all’oggetto di interesse. • public Image fetchLogo(String comcod): Estraggo il logo dalla tabella VUOSSTEMMI. • public void insertIntoTableVUOSSTEMMI(byte[] byteimgSmall,String comcod): Inserisce lo stemma nel db. • public void testImage(String comcod): Controlla se esiste già un immagine al posto nella posizione di quella che devo inserire e in caso affermativo la rimuove. • public Image fetchDB(String comcod, int progre,String type): Recupera l’immagine dal database. • public String[] getMail(String comune): Ottiene una lista di mail per l’invio delle notifiche. • public ArrayListhM aili getMailCom(String comune): Ottiene la lista delle mail di riferimento per il comune per la visualizzazione nell’interfaccia di gestione. • public void insertMail(String comcod, String mail,String descr): Inserisce nel database una mail per la notifica delle inserzioni. • public void removeMail(String comcod, String mail): Elimina dal database una delle mail per la notifica delle inserzioni. 7.4. VERSIONI JDBC USATE 63 Per gestire la corretta scrittura dei campi blob del database (in alcuni casi di cui faremo menzione nella sezione relativa ai database utilizzati) abbiamo predisposto una classe che implementa l’interfaccia Blob per renderla più flessibile per i nostri scopi. Nome Classe: ByteBlob.Java Descrizione: Implementazione dell’interfaccia blob che consente la costruzione dell’oggetto tramite un array di byte. Non sono stati aggiunti metodi estranei all’implementazione standard della classe. Per l’istanziazione del logger si è realizzata una classe apposita; per separarla logicamente dal controller si è optato per inserirla nel model. Nome File: Log.Java Descrizione: Classe usata per istanziare il Logger. Il suo costruttore si preoccupa di avviare il BasicConfigurator. Si è resa necessaria questa classe poiché il logger è studiato per essere istanziato nel main di un applicazione e trattandosi di una web application qualsiasi altra soluzione porta a una istanziazione multipla (causa di stampe multiple). 7.4 Versioni jdbc usate Di seguito sono presentati i package relativi alle librerie jdbc usate per ciascun DBMS e gli ambienti di sviluppo adoperati per la gestione di ciascun database. DB: Oracle Versione: 10.x.x Librerie: ojdbc14.jar Tool grafico usato per la gestione delle tabelle: Oracle SQL Developer DB: Postgres Versione: 8.1 Librerie: postgreSQL-8.1-409.jdbc3.jar Tool grafico usato per la gestione delle tabelle: pgAdmin3 DB: MySQL Versione: MySQL-server-5.0 Librerie: mySQL-connector-Java-5.0.5-bin.jar Tool grafico usato per la gestione delle tabelle: MySQL-navigator 7.5 Codice per la connessione Il metodo Connect(), della classe UtServDB, a seguito dell’introduzione del supporto a MySQL e Postgre è stato modificato nel seguente modo: 64 CAPITOLO 7. DATABASE public Connection connectDB () { log.info("Tentativo connessione database"); Connection conn=null; try{ switch(this.dbType){ case ORACLE: { //Uso driver oracle DriverManager. registerDriver(new oracle.jdbc.driver.OracleDriver()); String connessione = "jdbc:oracle:thin:@"+dbip+":"+dbPort+":"+dbname; conn=DriverManager.getConnection(connessione, user, pass); this.TODATE="to_date"; this.TOSTRING="to_char"; this.formatDate="dd/mm/yyyy"; break; } case POSTGRE: { //Uso dirver Postgre String connessione = "jdbc:postgreSQL://"+dbip+":"+dbPort+"/"+ +dbname; log.info(connessione); Properties prop = new Properties(); prop.setProperty("user",user); prop.setProperty("password",pass); DriverManager.registerDriver(new org.postgreSQL.Driver()); conn = DriverManager.getConnection(connessione, prop); this.TODATE="to_date"; this.TOSTRING="to_char"; this.formatDate="dd/mm/yyyy"; break; } case MYSQL: { //Uso driver MySQL String connessione = 7.5. CODICE PER LA CONNESSIONE 65 "jdbc:mySQL://"+dbip+":"+dbPort+"/"+dbname; Properties prop = new Properties(); prop.setProperty("user",user); prop.setProperty("password",pass); DriverManager.registerDriver(new com.mySQL.jdbc.Driver()); conn = DriverManager.getConnection(connessione, prop); this.formatDate="%m/%d/%Y"; this.TODATE="STR_TO_DATE"; this.TOSTRING="DATE_FORMAT"; break; } default: log.error("Database non selezionato"); break; } } catch(Exception e) { log.error("Eccezione nella connessione a database: "); e.printStackTrace(); } log.info("Connessione effettuata con successo"); return conn; } I valori utilizzati per creare le stringhe di connessione ai vari database sono ottenuti tramite il reperimento dei valori da un file di properties (tale reperimento avviene nel costruttore della classe con accorgimenti analoghi a quelli adottati per il file .jrxml dei report). Il file di properties definito è il seguente: ########################## # IP Database IP = 10.0.1.54 # IP = localhost ########################## # Configurazione Postgre # DbPort = 5432 66 CAPITOLO 7. DATABASE ########################## # Configurazione MySQL # DbPort = 3306 ########################## # Configurazione Oracle DbPort = 1521 ########################## # Dati DB,User e Pwd # ORACLE DbName = db1 User = ostest Pwd = ostest # # # # Postgre DbName = ragnarok User = ragnarok Pwd = ragnarok # # # # MySQL DbName = smarriti User = ragnarok Pwd = ragnarok ########################## # Tipo DB usato # 0 = Oracle # 1 = Postgre # 2 = MySQL DbType = 0 7.6 Struttura Database La struttura del database è la seguente: • VUOSDEPOSITI: informazioni sui depositi utilizzati per la raccolta degli oggetti smarriti 7.6. STRUTTURA DATABASE 67 • VOUSIMMAGINI: immagini allegate alle denunce di smarrimento/ritrovo • VUOSOGGETTI: informazioni relative agli oggetti e alle denunce • VUOSPROGRESSIVI: progressivo per l’inserzione di una denuncia nel comune • VUOSSTEMMI: immagini degli stemmi dei comuni • VUOSTIPI: informazioni sulle categorie e subcategorie • VUOSVERBALI: contiene i pdf dei verbali 2 • VUOSMAIL: informazioni sulle mail per le comunicazioni riguardanti gli oggetti inseriti. Inoltre è stata usata la seguente view: VUOSCOMUNI: informazioni sui comuni e la seguente sequence: VOUSIMMSEQ: gestisce il numero progressivo di inserzione utilizzato dalla tabella3 . A causa delle inevitabili differenze dei tipi relativi ai campi di ciascuna tabella tra i tre database abbiamo deciso di riportare qui come esempio la struttura relativa alle tabelle su Oracle. Per completezza sono presenti nella cartella /WEB-INF/conf/Script/ gli script per la ricostruzione delle tabelle per ciascuno dei database utilizzati. Ecco la struttura delle tabelle e descrizione di ciascun campo: Campi tabella VUOSDEPOSITI comcod: (Number 6,0 - Not null - PK) codice comune di riferimento deprog: (Number 6,0 - Not null - PK) progressivo del deposito descri: (Varchar 50 - Not null) nome deposito indir: (Varchar 50 - Not null) indirizzo deposito refe: (Varchar 30 - Not null) referente per il deposito tele: (Varchar 20 - Not null) numero di telefono email: (Varchar 40 - Not null) email di riferimento Campi tabella VUOSIMMAGINI id: (Number 10,0 - Not null - PK) 2 Non più usata nella nuova versione, i pdf sono generati dinamicamente. Non necessaria in MySQL poiché è stato usato un campo autoincrement sulla tabella delle immagini. 3 68 CAPITOLO 7. DATABASE comcod: (Number 6,0 - Not null ) codice del comune di riferimento progre: (Number 6,0 - Not null) progressivo della denuncia di riferimento img: (Blob) binario dell’immagine descrizione: (Varchar 100) descrizione dell’oggetto imgsmall:(Blob) binario dell’immagine rimpicciolita Campi tabella VUOSOGGETTI comcod: (Number 6,0 - Not null ) codice del comune progre: (Number 6,0 - Not null ) progressivo denuncia deprog: (Number 3,0) progressivo del deposito di giacenza categoria: (Varchar 20 - Not Null) categoria oggetto subcategoria: (Varchar 20 - Not Null) subcateogria oggetto descrizione: (Varchar 4000 - Not Null) descrizione oggetto opera1: (Varchar 30 - Not Null) operatore che ha inserito la denuncia opera2: (Varchar 30) operatore che ha modificato la denuncia datains: (Date -Not Null) data inserimento della denuncia tipope: (Number 1,0 - Not null ) tipo denuncia (0 smarrimento / 1 ritrovo) rinome: (Varchar 50) nome e cognome del ritrovante riadd: (Varchar 50) indirizzo ritrovante rinas: (Date) data di nascita ritrovante ricom: (Number 6,0) comune nascita ritrovante ritel: (Varchar 20) telefono ritrovante rimail: (Varchar 30) email ritrovante datrit: (Date) data ritrovo oggetto luogo: (Varchar 100) luogo ritrovo oggetto publi1: (Date) data della prima pubblicazione della denuncia publi2: (Date) data seconda pubblicazione della denuncia prnome: (Varchar 50) nome proprietario pradd: (Varchar 50) indirizzo proprietario prnas: (Date) data nascita del proprietario prcom: (Number 6,0) comune nascita del proprietario prtel: (Varchar 20) telefono proprietario prmail: (Varchar 30) email proprietario prdoc: (Varchar 50) documento proprietario pravvi: (Date) data della denuncia da parte del proprietario flagbi: (Number 1,0 - Not null ) visibilità scheda (1 visibile / 0 invisibile) flagend: (Number 1,0) pratica conclusa (1 conclusa / 0 non conclusa) ricomres: (Number 6,0) comune residenza ritrovante riverb: (Varchar 50) prdeleg: (Varchar 50) delegato proprietario ricodfis: (Varchar 16) codice fiscale ritrovante 7.6. STRUTTURA DATABASE 69 flagenddata: (Date) data chiusura pratica prcodfis: (Varchar 16) codice fiscale proprietario prcomres: (Number 6,0) comune residenza proprietario ridoc: (Varchar 50) documento ritrovante ricogn: (Varchar 30) cognome ritrovante (non più usato) datasmarr: (Date) data smarrimento luogosmarr: (Varchar 100) luogo smarrimento pravviprot: (Number 6,0) prdencomando: (Varchar 40) comando dove è stata effettuata la denuncia prdendata: (Date) data della denuncia Campi tabella VUOSPROGRESSIVI comcod: (Number 6,0 - Not Null - PK) codice comune progre: (Number 6,0 - Not Null - PK) progressivo da utilizzare per un comune descr: (Varchar 50) eventuale descrizione Campi tabella VUOSSTEMMI comcod: (Number 6,0 - Not Null - PK) codice comune stemma: (Blob) stemma piccolo del comune descrizione: (Varchar 30) descrizioni varie tophtml: (Blob) immagine intestazione pagina html bottomhtml: (Blob) immagine di fondo della pagina html stemmino: (Blob) stemma 10x13 del comune jpeglogotop: (Blob) immagine per la testata di un documento pdf jpeglogobottom: (Blob) immagine piè di pagina documento pdf Campi tabella VUOSTIPI categoria: (Varchar 20 - Not Null - PK) nome categoria subcategoria: (Varchar 20 - Not Null - PK) nome subcategoria Campi tabella VUOSUTENTI comcod: (Number 6,0 - Not Null - PK) codice comune username: (Varchar 20 - Not Null - PK) nome utente password: (Varchar 20 - Not Null) password utente descrizione: (Varchar 50) eventuali commenti Campi tabella VUOSVERBALI comcod: (Number 6,0 - Not Null) codice comune progre: (Number 6,0 - Not Null) progressivo verbale descrizione: (Varchar 50 - Not Null) tipo verbale testo1: (Blob) testo2: (Blob) testo3: (Blob) 70 CAPITOLO 7. DATABASE Campi tabella VUOSMAIL mail: (Varchar 50 - Not Null - PK) indirizzo mail comcod: (Varchar 50 - Not Null - PK) codice comunale descr: (Varchar 50) descrizione dell’indirizzo (referente) Campi view VUOSCOMUNI comcod: (Number 6,0 - Not Null) codice comune comdes: (Varchar 28) descrizioni varie compro: (Varchar 2) provincia comcap: (Number 5,0) codice avviamento postale comcat: (Varchar 4) comcom: (Number 6,0) 7.7 Sequence e Autoincrement La sequence è un oggetto presente in Oracle e Postgre che genera numeri progressivi ed è utilizzato per creare chiavi primarie. Per tale uso è necessario creare una sequence corrispondente alla tabella a cui si vuol associare la chiave primaria e, ad ogni inserimento in tale tabella, incrementare il valore della sequence dello step desiderato (fissato alla creazione). In altri database (tra cui MySQL) le sequence sono sostituite con campi auto incrementanti interni alla tabella stessa. Pur cambiando la metodologia di salvataggio su database la funzione svolta è la medesima. 7.8 Gestione campi Blob Per gestire il salvataggio delle immagini nel database ho fatto uso di campi di tipo blob. Le implementazioni a livello di DBMS di questo tipo di dato hanno costituito una delle maggiori problematiche per la gestione delle inserzioni e il reperimento dei dati incontrate durante la realizzazione del servizio. Ecco quali sono per ogni database le particolarità incontrate e le soluzioni trovate per la gestione consistente delle immagini: 1. Oracle In Oracle l’aspetto che differisce maggiormente dagli altri database è relativo all’inserimento degli oggetti in un campo Blob. E’ necessario, infatti, eseguire tale azione in due fasi (disabilitando l’autocommit); la prima fase consta nella creazione all’interno del database di un 7.8. GESTIONE CAMPI BLOB 71 empty blob() e la seconda dell’update di tale campo e della effettiva memorizzazione dell’oggetto desiderato. Di seguito il codice: con.setAutoCommit(false); //Prima query, creazione degli empty_blob. String insert = "insert into "+nomeTabella+ "(COMCOD, PROGRE, DESCRIZIONE, IMG, IMGSMALL, ID) " +"values ("+ "+comcod+"’"+","progre+"’"+"," +descrizione+"’"+","+"empty_blob()"+","+ "empty_blob()"+","+imgID+")"; log.debug("eseguo la query: "+insert); stmt.execute(insert); //Seconda query, update dei campi blob String cmd ="SELECT IMG, IMGSMALL FROM " +nomeTabella+ " WHERE COMCOD="+comcod+" AND PROGRE="+progre+ " AND ID="+imgID; log.debug("eseguo la query: "+cmd); ResultSet rset = stmt.executeQuery(cmd); rset.next(); Blob blobBig = rset.getBlob("IMG"); Blob blobSmall = rset.getBlob("IMGSMALL"); log.debug("ottengo l’outputStream per le scritture"); OutputStream outstreamBig = blobBig.setBinaryStream(blobBig.length()); OutputStream outstreamSmall = blobSmall.setBinaryStream(blobSmall.length()); log.debug("eseguo le scritture"); outstreamBig.write(bufferBig); outstreamSmall.write(bufferSmall); log.debug("mi assicuro di aver scritto tutti i dati"); outstreamBig.flush(); outstreamSmall.flush(); 72 CAPITOLO 7. DATABASE log.debug("chiudo i canali di out"); outstreamBig.close(); outstreamSmall.close(); log.debug("chiudo la connessione al db"); con.commit(); con.setAutoCommit(true); con.close(); 2. Postgre In Postgre il principale problema è dovuto al fatto che non esiste un implementazione dei Blob simile a quella degli altri database. Il tipo blob è infatti sostituito dai tipi Oid e bytea la cui unica differenza è la dimensione massima dell’oggetto che può essere memorizzato. A seconda delle implementazioni dei driver jdbc usati il tipo di dato Blob utilizzato da Java viene identificato con l’uno o con l’altro dei due tipi presenti sul database. Nel nostro caso Blob viene associato al tipo Oid. Di seguito il codice usato: con.setAutoCommit(false); getImgID ="select nextval(’VUOSIMMSEQ’)"; //ottengo il successivo nella sequence ResultSet rsID = stmt.executeQuery(getImgID); rsID.next(); imgID = rsID.getInt(1); //ByteBlob e’ una implementazione del tipo Blob definita //in it.grosseto.comune.db ByteBlob big = new ByteBlob(bufferBig); ByteBlob small = new ByteBlob(bufferSmall); PreparedStatement ps = con.prepareStatement ("insert into VUOSIMMAGINI(comcod,progre, descrizione,img,imgsmall,id) values (?,?,?,?,?,?)"); ps.setString(1, comcod); ps.setInt(2, progre); 7.8. GESTIONE CAMPI BLOB 73 ps.setString(3, descrizione); ps.setBlob(4, big); ps.setBlob(5, small); ps.setInt(6, imgID); ps.executeUpdate(); con.commit(); con.setAutoCommit(true); return; Inoltre in Postgre per recuperare il valore di un campo Oid è necessario usare degli oggetti specifici resi disponibili dai driver, il codice per la lettura diviene quindi: con.setAutoCommit(false); LargeObjectManager lobj = ((org.postgreSQL.PGConnection)con).getLargeObjectAPI(); String str = " SELECT " +type+" FROM "+nomeTabella+ " WHERE PROGRE="+progre+" and COMCOD=’"+comcod+"’"; Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(str); rs.next(); // Apro il large object in lettura long oid = rs.getInt(1); LargeObject obj = lobj.open(oid, LargeObjectManager.READ); // Leggo i dati byte buf[] = new byte[obj.size()]; obj.read(buf, 0, obj.size()); obj.close(); rs.close(); con.commit(); con.setAutoCommit(true); 3. MySQL In MySQL per scrivere un campo Blob con i driver jdbc è necessario usare il metodo setBytes(byte[] b) al posto di setBlob(Blob b) pertanto il codice risulta essere: 74 CAPITOLO 7. DATABASE //non ho necessita di calcolare il successivo, //la tabella e’ auto-incrementante con.setAutoCommit(false); PreparedStatement ps = con.prepareStatement("insert into VUOSIMMAGINI (COMCOD, PROGRE,DESCRIZIONE, IMG, IMGSMALL) values (?,?,?,?,?)"); ps.setString(1, comcod); ps.setString(2, ""+progre); ps.setString(3, descrizione); ps.setBytes(4, bufferBig); ps.setBytes(5, bufferSmall); ps.executeUpdate(); con.commit(); con.setAutoCommit(true); return; 7.9 Conversioni String-Date e annotazioni MySQL utilizza delle funzioni di conversione tra String e Date che hanno nomi diversi da quelle usate da Postgre e Oracle. Per le conversioni da String a Date si ha: Postrgres-Oracle: to date MySQL: STR TO DATE Per le conversioni da String a Date si ha: Postrgres-Oracle: to char MySQL: DATE FORMAT Altre particolarità da prendere in considerazione riguardano i nomi attribuibili alle tabelle: Postgres è, infatti, a differenza di Oracle e MySQL case-sensitive per quanto riguarda i nomi delle tabelle (non dei campi) pertanto ho deciso di utilizzare nomi in maiuscolo all’interno delle query SQL prodotte in modo da renderle portabili senza problemi su tutti e tre i DBMS utilizzati. 7.10. MODIFICHE E POSSIBILI MIGLIORIE ALLA STRUTTURA DEL DATABASE75 7.10 Modifiche e possibili migliorie alla struttura del Database La tabella VUOSOGGETTI contiene informazioni relative a tre entità di tipo distinto: Proprietario, Ritrovante e Informazioni sull’oggetto e la sua denuncia. A seguito di tale osservazione sarebbe possibile la suddivisione in tre tabelle: 1. VUOSOGGETTI: contenente le informazioni sull’oggetto e sulla denuncia (tipo,data,stato). 2. VUOSPROPRIETARI: contenente le informazioni sui proprietari che hanno sporto denunce di smarrimento 3. VUOSRITROVANTI: contenente informazioni sui ritrovanti che hanno sporto denuncia di ritrovo. Operando una tale suddivisione si eviterebbe la possibilità di ridondanza riguardante le informazioni sui proprietari e sui ritrovanti ed inoltre ci si atterrebbe ad un approccio di tipo OO-DBMS, facilitando una sostituzione delle query SQL con una gestione del model sul tipo offerto da Framework come Hibernate. La struttura cosı̀realizzata prevederebbe all’interno di VUOSRITROVANTI e di VUOSPROPRIETARI chiavi esterne che referenzino la tupla di VUOSOGGETTI contenente le informazioni relative alla denuncia. Attualmente tale approccio è utilizzato per le informazioni sui depositi (deprog è infatti chiave esterna per la tabella VUOSDEPOSITI). Osservando i dati memorizzati e la precedente implementazione dell’applicazione si può notare come alcuni campi di questa tabella inoltre non siano più utilizzati4 . 4 E’ il caso di prcogn e ricogn, poiché quello che dovrebbe essere il loro contenuto è stato assimilato a quello di altri campi. 76 CAPITOLO 7. DATABASE Capitolo 8 Sistema di Logging Una volta terminata la prima versione dell’applicazione ho proceduto alla registrazione di un Logger per rendere un eventuale successivo debug (e modifica del codice) di più rapida applicazione. Rimanendo nel contesto OpenSource, la libreria utilizzata è stata log4j, fornita anche essa (al pari di MyFaces e Tomcat), dalla Apache Foundation. La gestione del logger tramite tale package risulta di facile e rapida realizzazione, l’unica difficoltà, dovuta alla tipologia del progetto, avviene al momento della registrazione del BasicConfigurator, oggetto necessario per ottenere e configurare il canale di out per i messaggi del logger. Il BasicConfigurator, infatti, è studiato per essere istanziato una sola volta all’inizio del ramo principale dell’applicazione (il metodo main in una qualsiasi programma stand-alone) e, nel contesto di una web application, è necessario assicurare che esso non venga registrato più di una volta per evitare una inutile ridondanza di output. Per ovviare a questo problema abbiamo introdotto la classe Log (all’interno del package it.grosseto.comune.db) che, tramite i consueti meccanismi dei JavaBeans, viene inizializzata come prima entità del progetto al caricamento della pagina principale (sia della parte pubblica sia di quella privata) e si occupa esclusivamente di registrare tale componente. Log4J offre la possibilità di inserire annotazioni suddividendole in più livelli di criticità per rendere possibile la realizzazione di log per diversi usi. Ho utilizzati solo tre dei cinque livelli offerti dalla libreria (i cinque livelli sono: INFO, DEBUG, WARN, ERROR, FATAL) e in particolare: INFO: stampe di informazione alla chiamata dei metodi di gestione dei bean e dei costruttori. DEBUG: per descrivere i punti salienti del flusso di applicazione. ERROR: per segnalare le eccezioni lanciate dall’applicazione. 77 78 CAPITOLO 8. SISTEMA DI LOGGING Gli altri due livelli non sono stati usati poiché le situazioni che possono presentare problemi non dannosi al flusso dell’applicazione (WARN) sono trattati con la gestione delle eccezioni (e quindi sollevano un messaggio ERROR) e non sono presenti parti di codice che possano provocare errori in grado di terminare l’applicazione (FATAL). Di seguito riporto il codice usato per l’inizializzazione del BasicConfigurator (esempi della definizione di annotazioni di livello INFO e DEBUG nel progetto sono state mostrate precedentemente, seppure non presentate ufficialmente, nel codice riportato nella relazione): private static Logger log = Logger.getRootLogger(); public Log(){ BasicConfigurator.configure(); log.info("Istanziato il logger"); } Parte IV Tecnologie alternative sperimentate 79 Capitolo 9 Enterprise Java Bean 3 9.1 Scelta dell’Application Server Per poter utilizzare EJB ho necessariamente dovuto passare da un servlet/JSP container (Tomcat) ad un application server che fornisse il supporto per tali entità. Prima di iniziare la fase di implementazione di questa seconda versione del progetto, ho preso in esame due prodotti diversi tra loro che potessero soddisfare i requisiti richiesti: Jonas e Jboss. 9.1.1 Jonas Jonas non può essere definito un application server nel senso pieno del termine; Jonas è il nome di un progetto che mira, tramite l’introduzione di uno specifico package, a estendere Tomcat con il supporto per gli EJB3. Le caratteristiche di Jonas erano più che soddisfacenti per il semplice caso di studio che avrei dovuto sviluppare ma la mancanza di una buona documentazione relativa al corretto deployment degli EJB e le poche fonti di informazioni disponibili sul web mi hanno sconsigliato di intraprendere questa scelta. 9.1.2 Jboss Il secondo candidato è stato Jboss. Questo application server offre una ampia gamma di documentazione e tutorial seppure, altra faccia della medaglia, risulta molto più pesante da gestire rispetto allo snello e più limitato nelle funzioni Jonas. Per semplificare la gestione del deployment degli EJB e del progetto su tale application server abbiamo deciso di rimuovere dalla nuova implementazione la parte relativa all’autenticazione. 81 82 CAPITOLO 9. ENTERPRISE JAVA BEAN 3 A seguito di alcune prove di configurazione di entrambi gli ambienti la scelta è quindi ricaduta su Jboss. 9.2 Implementazione La scelta dell’introduzione di questa nuova tecnologia nel servizio web realizzato è stata realizzata nel seguente modo: ho previsto una separazione della logica contenente le query in SQL al database dal resto dell’unità applicativa rendendo la classe preposta a tale scopo uno Stateless Session Bean. A seguito di tale modifica, ininfluente su un progetto dalle dimensioni limitate e non a carattere distribuito come il nostro, possiamo immaginare la logica dell’applicazione suddivisa in due livelli distinti: 1. Un lato client di cui fanno parte tutte le classi relative ai bean e che viene acceduto dall’interfaccia web 2. Un lato server di cui fa parte la classe UtServDB che si occupa di gestire le richieste ricevute e restituire le risposte al client Nella implementazione realizzata tale suddivisione è esclusivamente logica poiché entrambe le parti sono in esecuzione su di una stessa JVM ma, nel caso di applicazioni distribuite, questa soluzione è molto comoda dal punto di vista progettuale. Nel progetto ho usato degli Stateless Session Bean poiché non avevo la necessità di mantener traccia delle operazioni precedentemente effettuate dall’utente e il risultato ottenuto è analogo a quello che avrei potuto ottenere tramite l’implementazione di webservices che svolgessero funzioni analoghe. Seppure il risultato in questo caso è analogo le due tecnologie hanno utilizzo e finalità tra loro differenti. Per la realizzazione ho proceduto come segue: Per convertire la classe UtServDB in un EJB3 ho realizzato due interfacce (una remota e una locale) da far implementare alla classe stessa, reso serializzabili tutte le entità usate da tale classe e sostituito in esse la chiamata al suo costruttore con il sistema di lookup di JNDI che EJB3 usa per recuperare le proprie entità. 9.2.1 Annotation Gli EJB3 fanno uso di Annotation per semplificare il deployment delle classi sul server e per sostituire i file xml di configurazione presenti sino alla versione EJB2.1. 9.2. IMPLEMENTAZIONE 83 Le annotation (indicate tramite il simbolo ’@’) sono direttive che vengono interpretate dal compilatore e dal server per recuperare delle informazioni relative alle entità a cui sono applicate. Oltre a quelle definite dalla Sun possono essere definite nuove annotation dagli sviluppatori. 9.2.2 Codice Nello specifico mostro le due interfacce, la nuova intestazione della classe UtServDB e un esempio di lookup. //Interfaccia Remota: package it.grosseto.comune.db; import it.grosseto.comune.bean.*; import Java.SQL.Connection; import Java.util.ArrayList; import Javax.ejb.Remote; @Remote public interface UtServDBRemote { public Connection connectDB (); public ArrayList getComuni(); public ArrayList getCategorie(); public ArrayList searchItems(Item a); public Item searchDetail(Item item); public void insertItem(Item item); public int getProgre(String comcod); public Deposito LoadDeposito(String comcod,String deprog); public ArrayList ValidateUser(Utente utente); public ComuneList getComDescr(ArrayList<String> comuni); public void updateItem(Item item); public int countValidateItem(); public ArrayList searchItemsValidate(String comcod); public ArrayList<Deposito> getDepositi(String comcod); public void saveDeposito(Deposito deposito); public void deleteDeposito(Deposito deposito); public void editDeposito(Deposito deposito); public String getDepDescr(String comcod,String deprog); public String test(); public String getComNome(String string); } 84 CAPITOLO 9. ENTERPRISE JAVA BEAN 3 //Interfaccia Locale: package it.grosseto.comune.db; import Javax.ejb.Local; @Local public interface UtServDBLocal {} //Nuova definizione della classe UtServDB: @Stateless public class UtServDB implements UtServDBRemote,UtServDBLocal /*(i metodi contenenti le query SQL rimangono * invariati, il vantaggio * di questo approccio \‘e che i bean * vengono usati come client rispetto * all’EJB definito e pertanto non devono * risiedere necessariamente sullo * stesso application server, * ne girare sulla stessa jvm.) */ //Esempio di lookup: @SuppressWarnings("unchecked") private UtServDBRemote lookup(){ Hashtable env = new Hashtable(); //Definisco il Context env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); env.put(Context.PROVIDER_URL,localhost:1099"); UtServDBRemote lookup=null; Context context; try { //Creo il Context context = new InitialContext(env); //Eseguo il lookup lookup = (UtServDBRemote) context.lookup("UtServDB/remote"); } catch (NamingException e) { e.printStackTrace(); 9.2. IMPLEMENTAZIONE 85 } return lookup; } Analizziamo nel dettaglio il codice presentato: Interfacce: Si tratta di interfacce standard Java con l’aggiunta tramite Annotations della loro definizione (Remote o Local) e per questo prendono il nome di POJI (Plain Old Java Interface). Le annotations sostituiscono il file di descrizione in xml presente fino alla versione 2.1 di EJB e sono interpretate dal compilatore e dall’application server allo scopo di definire l’EJB deployato. UtServDB: La classe essendo già precedentemente un Java Bean (privo di attributi e con un costruttore vuoto) non ha subito sostanziali modifiche. Implementa adesso le due interfacce precedentemente definite e l’annotation @Stateless la identifica come POJO (Plain Old Java Object). Il nome per il suo lookup è il nome della classe stessa poiché non è diversamente specificato nell’annotation (tale modifica sarebbe potuta essere effettuata scrivendo @Stateless(name=“nuovo nome lookup” ). Lookup: Il reperimento dell’EJB sull’application server, effettuato mediante le politiche di naming e di lookup di JNDI (Java Name and Directory Interface), è stato implementato dove necessario tramite un metodo privato di classe (come mostrato precedentemente). Un alternativa sarebbe stata usare un file di configurazione (jndi.properties) con il seguente contenuto: Java.naming.factory.initial= org.jnp.interfaces.NamingContextFactor Java.naming.factory.url.pkgs= org.jboss.naming:org.jnp.interfaces Java.naming.provider.url=localhost:1099 9.2.3 Conclusioni Nel progetto definitivo ho deciso di non fare uso di EJB3 poiché il servizio offerto non necessita di mantenere traccia della cronologia delle richieste ricevute sul server. Le interazioni client-server sono infatti limitate alla fase di richiesta e risposta e gli oggetti creati possono essere distrutti successivamente al loro uso. In questo caso l’uso di Stateless Session Bean avrebbe il solo effetto di aumentare il carico di lavoro del server non migliorando le prestazioni del servizio e complicando la logica applicativa del progetto. 86 CAPITOLO 9. ENTERPRISE JAVA BEAN 3 Capitolo 10 Gestione della Persistenza 10.1 Hibernate 10.1.1 Implementazioni Non potendo riprogettare il database per l’applicazione sviluppata (il vecchio database è tuttora in uso e ciò avrebbe comportato un interruzione di servizio) abbiamo deciso di introdurre Hibernate solo come caso di studio per le query riguardanti la parte relativa alla tabella VUOSDEPOSITI contenente informazioni sui depositi gestiti dai comuni. Abbiamo quindi seguito un approccio di reverse engeneering, facilitato dal tool di sviluppo MyEclipse che si occupa della compilazione di tutti i file xml destinati al mapping delle tabelle e alla creazione delle classi relative al mapping definito. Avendo a disposizione tali classi (dei POJO1 strutturati come bean) il passo successivo è stato scrivere una classe (HibernateDBCall) i cui metodi rimpiazzassero quelli utilizzati in UtServDB per le query SQL relative alla tabella da noi mappata. Un esempio di metodo implementato è il seguente: public void deleteDeposito(Deposito deposito) { Session s = HibernateSessionFactory.getSession(); try{ s.beginTransaction(); 1 Plain Old Java Object: una classe Java avente metodi e attributi relativi al suo funzionamento non Enterprise, ovvero un componente senza alcun legame con il container in cui viene utilizzato. 87 88 CAPITOLO 10. GESTIONE DELLA PERSISTENZA Criteria crit = s.createCriteria(Vuosdepositi.class); crit.add(Restrictions. like("deprog",deposito.getDeprog()+"%")); List<Vuosdepositi> app = crit.list(); s.delete(app.get(0)); s.getTransaction().commit(); }catch(Exception e){ s.getTransaction().rollback(); } } I passaggi necessari ad effettuare, in questo caso, la rimozione del deposito dal database sono i seguenti: 1. reperimento della Session di Hibernate (come vedremo in seguito tale Session non coincide con quella di JSF e per tale motivo si ha la necessità di utilizzare, in alcuni casi, qualche piccolo accorgimento); 2. iniziare una transazione sulla sessione definita; 3. stabilire dei criteri per l’interrogazione del database (nel costruttore dell’oggetto Criteria viene definita la classe, e quindi la tabella, a cui fare riferimento per la query mentre tramite il metodo add è possibile definire tutte le clausole che possono essere utilizzate in una query SQL); 4. definire il tipo di query (update/insert/delete o, se non specificato altro, select); 5. effettuare il commit o il rollback. I risultati di una query vengono ritornati all’interno di una List. 10.1.2 Note sulla sessione Hibernate Gli oggetti definiti durante le transazioni da Hibernate hanno un ciclo di vita differente rispetto a quelli definiti da JSF e questo causa, in alcuni casi, dei problemi relativi al reperimento di tali oggetti. Il problema, nel nostro caso si verifica per la funzione di modifica dei valori assegnati ad un deposito. Tale funzione è cosı̀progettata: 10.2. IBATIS 89 • nella pagina di gestione dei depositi è presente una dataTable popolata al caricamento dai dati provenienti dal database relativi ai depositi presenti per il comune, • selezionando la descrizione del deposito si accede ad una pagina per la modifica dei suoi dati. A seguito dell’accesso a questa pagina, e riempito il form di modifica, quando viene effettuato il submit Hibernate riconosce in sessione un oggetto precedentemente definito dallo stesso nome (risultato della query precedente effettuata per popolare la dataTable) e si rifiuta di eseguire l’update. La soluzione proposta a tale problema è stata quella di effettuare un clean della sessione di Hibernate prima di eseguire l’update dell’oggetto. Il metodo clear non invalida la sessione JSF, che contiene dati necessari al corretto funzionamento dell’applicazione, ma solo quella di Hibernate poiché le due sono definite in modo indipendente e non sono tra loro strettamente correlate. 10.1.3 Chiavi esterne Hibernate nel mapping da tabella a POJO mantiene i riferimenti presenti come chiavi esterne prevedendo per tali campi nella classe un oggetto del tipo relativo alla tabella a cui la chiave fa riferimento. Questo approccio rende possibile effettuare interrogazioni complesse in modo OO mantenendo semplice la modalità di accesso ai campi degli oggetti risultato delle interrogazioni. Questo middleware consente anche il mapping per chiavi composte, in questo caso però diviene più macchinosa la gestione del riferimento (non è più possibile associare un oggetto direttamente ai campi relativi alla chiave esterna). 10.2 iBatis Riprendendo l’approccio adottato per l’introduzione di Hibernate, ho eseguito la riconversione solo della parte riguardante la gestione dei Depositi. Questa scelta è dovuta principalmente alla presenza di query per la cui composizione, nel codice sviluppato, si procede a vari controlli sui parametri in ingresso: riprodurre in iBatis tali query avrebbe causato un lavoro molto dispendioso e si è quindi deciso di verificare solo l’usabilità e i vantaggi presentati da questo Framework. 90 CAPITOLO 10. GESTIONE DELLA PERSISTENZA 10.2.1 XML e configurazioni iBatis necessità di un file di configurazione (ibatis-config.xml ) per definire i parametri di connessione al database e di un ulteriore file xml per ogni entità di cui si desidera effettuare il mapping. Tali files si occupano di far corrispondere i campi delle tabelle alle proprietà dei bean prodotti e di definire per tali entità le query SQL da richiamare nel codice. Vediamo adesso in ordine i file xml prodotti: ibatis-config.xml: <SQLMapConfig> <settings cacheModelsEnabled="true" maxRequests="32" maxSessions="100" maxTransactions="100"/> <transactionManager type="JDBC"> <dataSource type="SIMPLE"> <property name="JDBC.Driver" value="com.mySQL.jdbc.Driver"/> <property name="JDBC.ConnectionURL" value="jdbc:mySQL://localhost:3306/smarriti"/> <property name="JDBC.Username" value="ragnarok"/> <property name="JDBC.Password" value="ragnarok"/> </dataSource> </transactionManager> <SQLMap resource="Deposito.xml"/> </SQLMapConfig> Deposito.xml: <SQLMap namespace="Depositi"> <cacheModel id="cache" type="LRU"> <flushInterval hours="24"/> </cacheModel> 10.2. IBATIS <typeAlias alias="Dep" type="it.grosseto.comune.bean.Deposito"/> <resultMap id="rsDeposito" class="Dep"> <result property="email" column="email"/> <result property="refe" column="refe"/> <result property="indir" column="indir"/> <result property="descr" column="descri"/> <result property="comcod" column="comcod"/> <result property="deprog" column="deprog"/> <result property="tele" column="tele"/> </resultMap> <select id="getListaDepositi" resultMap="rsDeposito" parameterClass="Java.lang.String"> select * from VUOSDEPOSITI where COMCOD = #value# </select> <insert id="save" parameterClass="it.grosseto.comune.bean.Deposito"> insert into VUOSDEPOSITI (COMCOD,DEPROG,DESCRI,TELE,REFE,EMAIL,INDIR) values (#comcod#,#deprog#,#descr#,#tele#,#refe#,#email#,#indir#) </insert> <delete id="remove" parameterClass="it.grosseto.comune.bean.Deposito"> DELETE FROM VUOSDEPOSITI WHERE COMCOD = #comcod# AND DEPROG = #deprog# </delete> <update id="update" parameterClass="it.grosseto.comune.bean.Deposito"> UPDATE VUOSDEPOSITI SET DESCRI = #descr#, TELE = #tele#, REFE = #refe#, 91 92 CAPITOLO 10. GESTIONE DELLA PERSISTENZA EMAIL = #email#, INDIR = #indir# WHERE COMCOD = #comcod# AND DEPROG = #deprog# </update> </SQLMap> Come si puø’ vedere l’incombenza relativa alla gestione delle connessioni al database e alla formulazione delle query viene delegata a questi file, permettendo cosı̀la loro modifica senza la necessità di alterare il codice dell’applicazione. Nella parte di definizione delle query questo framework si preoccupa di istanziare i PreparedStatement necessari e a effettuare il set dei valori ricevuti come parametri. 10.2.2 Modifiche apportate al codice A seguito della definizione dei file di configurazione è stato necessario apportare delle modifiche alla gestione degli accessi al database. I metodi della classe UtServDB sono divenuti superflui e sono stati sostituiti (per quanto riguarda la manipolazione degli oggetti di tipo Database e DatabaseList) da codice che segue il seguente pattern: SQLMapClient SQLMap = testIbatis.getInstance(); try { SQLMap.insert("save",this); } catch (SQLException e) { e.printStackTrace(); } testIbatis è una classe che si occupa di recuperare i file di configurazione illustrati in precedenza, di effettuarne il parsing e di predisporre l’uso della connessione cosı̀instanziata. E’ stata inserita nel package it.grosseto.comune.model . Capitolo 11 Framework utilizzati e il pattern MVC I Framework studiati durante il periodo di tirocinio mi hanno dato modo di analizzare diverse possibilità di applicazione del pattern MVC in cui i vari layer sono ottenuti di volta in volta con tecnologie diverse. La versione del servizio che viene resa disponibile al pubblico presenta la seguente suddivisione: • Model: Database e classe UtServDB • View-Controller: JSF e Java Beans E’ però possibile separare in modo più netto i vari layer optando, ad esempio, per le seguenti scelte progettuali: 1. Soluzione Hibernate • Model: Database e Hibernate • View: JSF e Java Beans • Controller: EJB3 2. Soluzione iBatis • Model: Database e iBatis • View: JSF e Java Beans • Controller: EJB3 93 94 CAPITOLO 11. FRAMEWORK UTILIZZATI E IL PATTERN MVC Le due soluzioni proposte sono solo alcune delle vare possibili. La parte di View in JSF può essere infatti sostituita con altri framework (come Struts o Velocity) e il controller non necessariamente deve essere realizzata tramite l’uso di EJB3. Nel caso specifico di JSF la parte di controller si può considerare integrata nel framework stesso che si occupa anche della View e quindi l’uso di altre tecnologie (quali ad esempio EJB3) è in molti casi superfluo e non apporta notevoli vantaggi al progetto stesso (tranne in casi paricolari). Per questo motivo, e a causa di quanto già discusso in precedenza, le scelte progettuali effettuate risultano le più consone al tipo di applicazione prodotta, seppur non consentono una separazione netta tra quella che è la parte applicativa e l’accesso ai database. Capitolo 12 Portlet 12.1 Introduzione I portlet sono moduli web riusabili all’interno di un portale Web. Ciascuna pagina di un portale viene suddivisa in una collezione di finestre, il contenuto di ognuna delle quali viene definito da un diverso portlet. Ogni portlet ospita una semplice applicazione web, realizzata con le tecnologie supportate dal portale, e può essere aggiunta, rimossa e spostata all’interno della pagina a piacimento dell’utente. A causa della sua natura il portale viene definito Portlet-Container: questo implica intrinsecamente che è il portale stesso che si occupa dell’interazione tra i portlet che contiene, dei meccanismi di autenticazione e della navigazione interna ai portlet. 95 96 CAPITOLO 12. PORTLET Per questo motivo i portlet presenti su un portale non sono raggiungibili da indirizzi predefiniti che esulino dalla loro visualizzazione all’interno del portale stesso e l’autenticazione per i servizi web offerti dai portlet non è più delegabile ai servizi stessi. Quest’ultimo inconveniente, oltre ad altri implementativi di cui parlerò nelle sezioni successive, ci hanno convinto a realizzare la versione portlet relativa esclusivamente alla parte pubblica dell’applicazione. 12.2 JetSpeed2 e jBoss Portal Per eseguire il deployment e il testing del portlet realizzato abbiamo preso in analisi due dei maggiori Portali OpenSource: Jetspeed2 fornito da Apache e jBoss Portal. Seppure la specifica relativa alle portlet sia stata fissata per rendere tali servizi web distribuibili su qualsiasi tipo di Portale effettuare il porting tra due diversi portali può necessitare modifiche dei file di configurazione prodotti, specialmente per quanto riguarda i bridge usati Poichè nel progetto è stata utilizzata l’implementazione fornita da Apache del framework JSF, per ottenere una migliore compatibilità ho optato per l’uso di Jetspeed2: come mostrerò nella sezione successiva è stato comunque necessario apportare delle modifiche all’applicazione per poter effettuare il porting. 12.3 MyFaces e Portlet Tra le varie tecnologie supportate da Jetspeed2 e jBoss Portal è presente anche JSF. Per convertire un applicazione web facente uso di JSF in una portlet è necessario utilizzare un particolare bridge, una libreria che si occupa di definire l’interazione tra il servizio e il portale, e di introdurre nel progetto dei descrittori xml che lo identifichino come servizio portlet. Purtroppo, seppure supportate nominalmente, le estensioni fornite dal pakage Tomahawk non sono fruibili in modo completo all’interno di un progetto portlet. In particolare: • inputCalendar: Non è possibile utilizzare il calendario in modalità popup. • inputFileUpload: Non è supportato 12.4. IMPLEMENTAZIONE 97 • dataScroller: Non è supportato completamente e quindi risulta non utilizzabile. • commandSortHeader: Non è supportato A causa dell’assenza di un supporto completo per questi componenti l’applicazione che ho convertito in portlet è stata semplificata prevedendo un uso limitato degli inputCalendar e eliminando completamente l’uso degli altri componenti non supportati. Per questo motivo la sola parte pubblica dell’applicazione è stata convertita in portlet. 12.4 Implementazione Effettuate le modifiche accennate nella sezione precedente, ho provveduto a definire i file xml di configurazione per la portlet: • portlet.xml: definisce il bridge e le view relative alla portlet • web.xml: alcune modifiche rendono possibile il caricamento del servizio web all’interno di un portale. Vediamo adesso il contenuto del file portlet.xml: <portlet-app id="Oggetti Smarriti" version="1.0"> <portlet id="Oggetti Smarriti"> <portlet-name>Oggetti Smarriti</portlet-name> <display-name>Oggetti Smarriti</display-name> <init-param> <name>ViewPage</name> <value>/Pub/index.jsf</value> </init-param> <portlet-class>org.apache.portals. bridges.jsf.FacesPortlet</portlet-class> <portlet-info> <title>Oggetti Smarriti</title> <short-title>Oggetti Smarriti</short-title> </portlet-info> </portlet> </portlet-app> 98 CAPITOLO 12. PORTLET hportlet − classi identifica la libreria utilizzata come bridge verso il portale: tale libreria varia a seconda del portale usato per fare in modo che il portlet possa supportare completamente gli stili e le peculiarità del portale su cui viene deployato. Vediamo le modifiche apportate al web.xml: <servlet> <servlet-name>JetspeedContainer</servlet-name> <servlet-class>org.apache.jetspeed.container. JetspeedContainerServlet</servlet-class> <init-param> <param-name>contextName</param-name> <param-value>TirocinioPortlet</param-value> </init-param> <load-on-startup>0</load-on-startup> </servlet> <servlet-mapping> <servlet-name>JetspeedContainer</servlet-name> <url-pattern>/container/*</url-pattern> </servlet-mapping> Oltre a queste modifiche è necessario introdurre numerosi hcontext − parami per rendere almeno in parte supportato il pakage Tomahawk1 . Ulteriori modifiche sono state apportate per il caricamento dei fogli di stile (css) e delle immagini visualizzate nell’applicazione: non esistendo un indirizzo relativo al portlet, come precedentemente chiarito, è stato necessario reimpostare i path per il reperimento dei componenti specificati utilizzando al posto di un indirizzamento relativo alla pagina uno assoluto avente come root la cartella principale del portlet. 1 Tali impostazioni non vengono riportate poiché, essendo ridotto l’uso di Tomahawk al minimo indispensabile, non hanno un grande impatto a livello di configurazione e non sono strettamente necessarie per la compilazione di un applicazione come portlet. Parte V Conclusioni 99 Capitolo 13 Stato attuale del progetto Nel momento in cui viene redatta questa relazione il servizio sviluppato è fruibile ai cittadini del comune di Grosseto. La pubblicazione del servizio web è avvenuta a seguito di alcuni test atti a verificarne l’effettiva funzionalità e stabilità in modo da limitare al minimo possibile i problemi dovuti all’upgrade del servizio precedente. La documentazione prodotta (Javadoc e appunti stesi durante la fase di progettazione) sono stati consegnati insieme alle versioni alternative del progetto (quelle sviluppate per soddisfare i casi di studio proposti) ai servizi informatici del comune per rendere possibile la manutenzione e l’aggiornamento del servizio successivamente al termine del tirocinio. La procedura di autenticazione tramite smart card non è stata testata durante il periodo di tirocinio ma essendomi attenuto alle specifiche ricevute in fase di progettazione e avendo testato il sistema di autenticazione di tipo “Single Sing On” tale verifica non è stata necessaria: la procedura necessaria sarà quindi impostata dal comune stesso. 101 102 CAPITOLO 13. STATO ATTUALE DEL PROGETTO Capitolo 14 Considerazioni sul lavoro svolto Durante lo svolgimento di questo tirocinio sono venuto in contatto con numerosi Framework, ambienti di sviluppo e tecnologie: questo mi ha dato modo di sviluppare un approccio critico alla progettazione di un applicazione web. La libertà decisionale, relativa alle scelte progettuali, concessami mi ha consentito di fare esperienza sull’organizzazione di un progetto di dimensioni non banali. Tale libertà, estesa anche alla scelta delle tecnologie da utilizzare per lo sviluppo dei casi di studio e all’ambiente di sviluppo, mi ha dato modo di valutare la bontà di tipologie diverse di soluzioni per i problemi proposti, consentendomi, a seguito della motivazione delle scelte effettuate, di apprendere le modalità d’uso dei framework che ritenevo interessanti sia da un punto di vista strettamente collegato al progetto sviluppato sia per quanto riguarda un interesse personale. Al termine del tirocinio ho sviluppato una buona esperienza alla programmazione J2EE, nella gestione di vari DBMS, nella configurazione di Server Web e di Application Server. Sono stato quindi in grado di mettere in pratica le competenze acquisite durante gli studi svolti e di ampliarle sia in ambito tecnico/pratico che per quanto riguarda l’analisi e il problem-solving. La mia valutazione personale di questa esperienza non può quindi che essere estremamente positiva, sia per quanto riguarda il lato formativo che per l’ambiente di lavoro e le persone che mi hanno aiutato e supportato durante lo svolgimento. 103 104 CAPITOLO 14. CONSIDERAZIONI SUL LAVORO SVOLTO Capitolo 15 Risorse 15.1 Ambiente di sviluppo, Ambienti di Deployment e Librerie Riporto di seguito le informazioni relative agli strumenti utilizzati per lo sviluppo ed il testing dell’applicazione. 15.1.1 Sviluppo Gli ambienti di sviluppo utilizzati sono stati i seguenti: Java Eclipse - Version: 3.2.2 + MyEclipse - Version: 5.1.1 GA Eclipse - Version: 3.1.2 + MyEclipse - Version: 4.1.1 GA JBossIDE - Version: 1.5.1 GA Report iReport - Version: 1.3.3 Gestione Database MySQL Navigator Oracle SQL Developer PgAdmin III 15.1.2 Deployment Gli ambienti di deployment utilizzati sono stati i seguenti: Servlet/JSP Container Apache Tomcat - Version: 5.5.23 Apache Tomcat - Version: 6.0.13 105 106 CAPITOLO 15. RISORSE Jonas - Version: 4.8.1 Application Server jBoss - Version: 4.2.0 Portali Jetspeed - Version: 2.1 15.1.3 Librerie Le seguenti librerie sono state utilizzate per lo sviluppo del progetto: Java Server Faces: myfaces-api-1.1.4.jar myfaces-impl-1.1.4.jar tomahawk-1.1.4.jar Progetto web (Librerie standard): activation.jar commons-beanutils-1.7.0.jar commons-codecs-1.3.jar commons-collections-3.1.jar commons-digester-1.6.jar commons-el-1.0.jar commons-fileupload.jar commons-lang-2.1.jar commons-logging-1.0.4.jar Javaee.jar jsp-2.0.jar jstl.jar mail.jar soap.jar standard.jar Driver jdbc: ojdbc14.jar postgreSQL-8.1-409.jdbc3.jar mySQL-connector-Java-5.0.5-bin.jar Hibernate: Hibernate 3.1 Core Libraries Hibernate 3.1 Advanced Support Libraries iBatis: ibatis-2.3.0.677.jar 15.2. DOCUMENTAZIONE E RIFERIMENTI 107 Jasper Report: jasperreports-1.3.3.jar Portlet: portals-bridges-JSF-1.0.1.jar Log4J: log4j-1.2.14.jar 15.2 Documentazione e riferimenti Per la realizzazione dell’applicazione e per la risoluzione dei problemi presentatisi ho fatto principalmente riferimento ai seguenti siti internet. [1] Apache MyFaces: http://myfaces.apache.org [2] Api Java EE 5: http://Java.sun.com/Javaee/5/docs/api/ [3] Esempi di uso dei componenti di MyFaces: http://www.irian.at/myfaces/ [4] Hibernate: http://www.hibernate.org [5] iBatis: http://ibatis.apache.org/ [6] Jasper Project: http://jasperforge.org [7] Jboss Portal: http://www.jboss.org/products/jbossportal [8] Jetspeed2: http://portals.apache.org/jetspeed-2 [9] Log4J: http://logging.apache.org/log4j/docs/ [10] Mokabyte: http://www.mokabyte.it [11] MySQL: http://www.mySQL.org [12] Oracle: http://www.oracle.com [13] OnJava: http://www.onJava.com [14] PostgreSQL: http://www.postgreSQL.org [15] Sito dell’application server JBoss: http://www.jboss.com [16] Tutorial Java EE 5: http://Java.sun.com/Javaee/5/docs/tutorial/doc/ A seguito del tirocinio è stato pubblicato l’articolo: [17] Dev 152:“Ordinare una DataTable”,Giulio Rossetti-Ludwig Bargagli 108 CAPITOLO 15. RISORSE Indice 1 Introduzione 1.1 Organizzazione della relazione . . . . . . . . . . . . . . . . . . 3 4 2 Requisiti del progetto 2.1 Dettagli precedente versione del servizio . . . . . . . . . . . . 2.2 Nuova implementazione . . . . . . . . . . . . . . . . . . . . . . 5 5 5 3 Tecnologie 3.1 Pattern MVC . . 3.2 Java Server Faces 3.3 JasperReport . . 3.4 Database . . . . . 3.5 Log4J . . . . . . 3.6 EJB3 . . . . . . . 3.7 Hibernate . . . . 3.8 iBatis . . . . . . I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La View 9 9 9 10 11 11 12 13 13 15 4 L’interfaccia utente rivolta al cittadino 4.1 Analisi dei Requisiti . . . . . . . . . . . . . . 4.2 Descrizione . . . . . . . . . . . . . . . . . . . 4.3 Dettagli implementativi form di Ricerca . . . 4.4 Dettagli dei componenti utilizzati nel progetto 4.5 Problemi riscontrati e Soluzioni proposte . . . 4.6 Struttura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 18 20 22 23 27 5 L’interfaccia utente rivolta agli operatori 29 5.1 Analisi dei Requisiti . . . . . . . . . . . . . . . . . . . . . . . 29 5.2 Descrizione . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 109 110 INDICE 5.3 5.4 5.5 5.6 II Dettagli implementativi . . . . . . . . . . . . . . . . 5.3.1 Inserzione denuncia smarrimento/ritrovamento 5.3.2 Ricerca oggetti presenti nel database . . . . . 5.3.3 Validazione schede immesse dall’utente . . . . 5.3.4 Interfaccia di Gestione . . . . . . . . . . . . . Problemi riscontrati e soluzioni proposte . . . . . . . Autenticazione . . . . . . . . . . . . . . . . . . . . . 5.5.1 Tomcat Autentication . . . . . . . . . . . . . 5.5.2 Single Sign On . . . . . . . . . . . . . . . . . Struttura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Il Controller 6 Logica dell’applicazione 6.1 Analisi dei Requisiti . . . . . . 6.2 I package . . . . . . . . . . . . . 6.3 Bean e Basket Bean . . . . . . . 6.4 Gestione delle immagini . . . . 6.5 Gestione delle email di servizio 6.6 Gestione dei report pdf . . . . . 6.6.1 Creazione di un report . III . . . . . . . . . . 43 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Il Model: gestione Database e Log 7 Database 7.1 Analisi dei Requisiti . . . . . . . . . . . . . . . . . . . . 7.2 Premessa . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3 Implementazione . . . . . . . . . . . . . . . . . . . . . . 7.4 Versioni jdbc usate . . . . . . . . . . . . . . . . . . . . . 7.5 Codice per la connessione . . . . . . . . . . . . . . . . . 7.6 Struttura Database . . . . . . . . . . . . . . . . . . . . . 7.7 Sequence e Autoincrement . . . . . . . . . . . . . . . . . 7.8 Gestione campi Blob . . . . . . . . . . . . . . . . . . . . 7.9 Conversioni String-Date e annotazioni . . . . . . . . . . . 7.10 Modifiche e possibili migliorie alla struttura del Database 8 Sistema di Logging 31 31 32 32 33 36 39 39 40 41 45 45 45 46 51 53 54 54 57 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 59 59 60 63 63 66 70 70 74 75 77 INDICE 111 IV 79 Tecnologie alternative sperimentate 9 Enterprise Java Bean 3 9.1 Scelta dell’Application Server 9.1.1 Jonas . . . . . . . . . 9.1.2 Jboss . . . . . . . . . . 9.2 Implementazione . . . . . . . 9.2.1 Annotation . . . . . . 9.2.2 Codice . . . . . . . . . 9.2.3 Conclusioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 81 81 81 82 82 83 85 10 Gestione della Persistenza 10.1 Hibernate . . . . . . . . . . . . . . . 10.1.1 Implementazioni . . . . . . . 10.1.2 Note sulla sessione Hibernate 10.1.3 Chiavi esterne . . . . . . . . . 10.2 iBatis . . . . . . . . . . . . . . . . . 10.2.1 XML e configurazioni . . . . . 10.2.2 Modifiche apportate al codice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 87 87 88 89 89 90 92 . . . . . . . . . . . . . . . . . . . . . 11 Framework utilizzati e il pattern MVC 12 Portlet 12.1 Introduzione . . . . . . . 12.2 JetSpeed2 e jBoss Portal 12.3 MyFaces e Portlet . . . . 12.4 Implementazione . . . . V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conclusioni . . . . 95 95 96 96 97 99 13 Stato attuale del progetto 101 14 Considerazioni sul lavoro svolto 103 15 Risorse 15.1 Ambiente di sviluppo, Ambienti di Deployment e Librerie 15.1.1 Sviluppo . . . . . . . . . . . . . . . . . . . . . . . 15.1.2 Deployment . . . . . . . . . . . . . . . . . . . . . 15.1.3 Librerie . . . . . . . . . . . . . . . . . . . . . . . 15.2 Documentazione e riferimenti . . . . . . . . . . . . . . . . . . . . . . . . . 105 . 105 . 105 . 105 . 106 . 107