Utilizzo dei Framework “Java Server Faces” ed “Hibernate”

Transcript

Utilizzo dei Framework “Java Server Faces” ed “Hibernate”
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