UNIVERSITÀ POLITECNICA DELLE MARCHE FACOLTÀ DI INGEGNERIA Corso di Laurea in Ingegneria Elettronica Dipartimento di Elettronica, Intelligenza Artificiale e Telecomunicazioni UN’ARCHITETTURA SOFTWARE COMPLETA PER TELECONSULTO E TELEREFERTAZIONE: PROGETTO ED IMPLEMENTAZIONE DEL REPOSITORY CENTRALE Tesi di Laurea di: Mirco Sturari Relatore: Prof. Aldo Franco Dragoni Correlatore: Prof. Paolo Puliti Anno Accademico 2004-05 INDICE Introduzione ........................................................................ 1 Capitolo 1. Telemedicina ...................................................... 2 1.1 Introduzione ................................................................... 3 1.2 Applicazioni .................................................................... 5 1.2.1 Vantaggi ................................................................... 5 1.2.2 Problemi.................................................................... 6 1.3 Stato dell’Arte................................................................. 8 1.4 Progetti dell’ ASUR Marche zona 7 ................................... 10 1.4.1 Sistema di teleconsulto ospedale di Osimo con stazioni fisse e stazioni mobili ................................................ 10 1.4.2 Assistente Virtuale .................................................... 13 1.4.3 Delocalizzazione della Refertazione Diagnostica............. 14 Capitolo 2. Specifiche progetto MiRo .................................. 17 2.1 Finalità ........................................................................ 18 2.2 Architettura generale ..................................................... 20 2.2.1 Repository ............................................................... 23 2.2.2 Laboratorio .............................................................. 24 2.2.3 Medico .................................................................... 26 2.3 Soluzioni tecniche.......................................................... 29 2.3.1 Comunicazione sicura e Mutuo Riconoscimento ............. 30 2.3.2 Messaggi SOAP ........................................................ 31 Capitolo 3. Implementazione del progetto.......................... 34 3.1 Repository.................................................................... 35 3.1.1 Concetto di evento.................................................... 35 3.1.2 Struttura della base-dati............................................ 38 3.2 Web Service ................................................................. 44 3.2.1 Web Service Repository ............................................. 44 3.2.2 Web Service Laboratory............................................. 49 -I- Capitolo 4. Tecnologie Software ......................................... 51 4.1 Java Platform................................................................ 52 4.1.1 Java lato server: le Servlet ........................................ 67 4.2 Service-Oriented Architecture ......................................... 74 4.2.1 Web Services ........................................................... 79 4.2.2 Protocollo SOAP........................................................ 84 4.3 Application Server ....................................................... 104 4.3.1 Apache Tomcat ...................................................... 104 4.3.2 Comunicazione SSL................................................. 118 4.4 Strumenti di Sviluppo .................................................. 131 4.4.1 Apache Axis ........................................................... 131 4.5 Database Relazionale ................................................... 139 4.5.1 MySQL .................................................................. 139 Conclusioni ...................................................................... 146 Appendice A. Codice sorgente .......................................... 159 A.1 Common Library ......................................................... 160 A.2 Web Service Repository................................................ 201 A.3 Web Service Laboratory ............................................... 205 Bibliografia ...................................................................... 207 - II - INTRODUZIONE Con il continuo sviluppo tecnologico, con la digitalizzazione e con la diffusione della banda larga è diventata forte la necessità di ristrutturare l’intero sistema sanitario nazionale. Questi fattori hanno dato nuovi impulsi al settore della Telemedicina, nell’ottica di una maggiore integrazione e una migliore efficienza dei servizi sanitari. Il progetto MiRo nasce dalla collaborazione tra ASUR zona 7 di Ancona e l’Università Politecnica delle Marche, per rispondere proprio a queste necessità. Abbiamo iniziato il nostro lavoro, essendo ingegneri, da un’analisi del concetto di telemedicina e da una panoramica della situazione attuale nel settore. Abbiamo quindi cominciato a progettare un’architettura software flessibile che potesse fungere da substrato alla realizzazione di un qualsiasi servizio sanitario delocalizzato, prendendo a riferimento uno dei rami di maggior interesse, la teleradiologia. Dal progetto si è passati all’implementazione, con la scelta delle tecnologie più idonee agli ambienti distribuiti ed eterogenei, i Web Service, e della piattaforma software più diffusa e portabile, Java. Una prima conferma della validità del progetto, la presentazione al convegno internazionale di telemedicina EMMIT06. -1- CAP. 1 - TELEMEDICINA CAPITOLO 1 TELEMEDICINA Introduzione al concetto di telemedicina, stato dell’arte nella sanità italiana e approfondimento dei progetti dell’ASUR Marche zona 7. 1.1 Introduzione 1.2 Applicazioni 1.2.1 Vantaggi 1.2.2 Problemi 1.3 Stato dell’Arte 1.4 Progetti dell’ASUR 1.4.1 Sistema di teleconsulto ospedale di Osimo con stazioni fisse e stazioni mobili 1.4.2 Assistente Virtuale 1.4.3 Delocalizzazione della Refertazione Diagnostica -2- CAP. 1 - TELEMEDICINA 1.1 INTRODUZIONE Una definizione della Telemedicina che ha riscosso un notevole successo è la seguente: la Telemedicina rappresenta l’erogazione di servizi sanitari, laddove la distanza rappresenti professionisti un fattore nell’assistenza critico sanitaria da che parte di utilizzino tecnologie dell’informazione e della comunicazione per lo scambio di trattamento informazioni e la rilevanti, prevenzione l’educazione continuativa nell’interesse del per delle degli la diagnosi, patologie e operatori miglioramento della il per sanitari, salute e delle comunità assistite. La Telemedicina telecomunicazione per utilizza le erogare tecnologie assistenza della sanitaria specialistica, spesso a notevoli distanze, con la possibilità di contenere i costi delle prestazioni. Questo avviene in special modo quando l’Assistenza Sanitaria è rivolta ad aree isolate o comunque dove non sia disponibile direttamente la prestazione specialistica del medico. La Telemedicina consente di conseguire notevoli benefici sia per erogatori di assistenza remota, sia naturalmente, per gli utenti dei servizi, siano essi pazienti o operatori sanitari. -3- CAP. 1 - TELEMEDICINA Lo sviluppo e l’applicazione della Telemedicina richiede significativi expertise nei settori delle telecomunicazioni, delle erogazioni dei servizi sanitari e dell’information tecnology and comunications. L’applicazione della Telemedicina ben si presta ad un opera di cooperazione fra i paesi dotati di maggiori tecnologie e quelli ancora in via di sviluppo. Fig. 1.1 – Telemedicina come servizio sanitario delocalizzato -4- CAP. 1 - TELEMEDICINA 1.2 APPLICAZIONI Il concetto di telemedicina non significa dunque soltanto “medicina praticata a distanza”, ma include tutti quegli aspetti legati all’assistenza sanitaria in senso lato. Una serie di nuovi concetti sono ora riferiti alla telemedicina; due di questi sono e-health e tele-health (tele-assistenza). La telemedicina/teleassistenza può essere applicata in vari modi in tutti i servizi sanitari; può facilitare e razionalizzare informazioni, la tra comunicazione servizi di e lo primaria scambio assistenza e di gli ospedali, tra ospedali diversi, tra ospedali e i laboratori, tra i servizi di riabilitazione e le organizzazioni paramediche. La telemedicina riguarda anche la direzione e l’amministrazione dell’unità sanitarie locali, i pazienti e i loro familiari. 1.2.1 Vantaggi L’avvento introduzione dell’informatica di nuove medica tecnologie e la conseguente nell’ambito sanitario hanno portato e porteranno notevoli vantaggi sia per i pazienti che per il personale medico. -5- CAP. 1 - TELEMEDICINA L’introduzione della telemedicina permette : Diagnosi e cure più rapide Minor numero di spostamenti sia del personale medico che dei pazienti Riduzione dei costi per personale (compreso quello di emergenza) Comunicazioni più veloci Aggiornamento più semplice e rapido delle informazioni riguardanti diagnosi e metodi di cura Miglior sostegno allo staff medico per la formazione sia teorica che pratica 1.2.2 Problemi In passato i costi per l’attrezzatura e le telecomunicazioni sono stati troppo alti da permetterne un’introduzione su larga scala, oggi tuttavia la situazione sotto questo punto di vista è notevolmente migliorata il vero ostacolo da affrontare è cercare di introdurre metodi che favoriscano l’accettazione e la comprensione da parte del personale interessato, delle applicazioni di telemedicina/teleassistenza nei servizi sanitari. Bisogna sottolineare che l’introduzione della telemedicina influenza i tradizionali metodi di lavoro, si vanno a modificare i modelli di cooperazione, si creano nuove figure -6- CAP. 1 - TELEMEDICINA lavorative e molto spesso sorgono problemi di distribuzione dei costi e di investimenti delle risorse. Alcuni progetti di telemedicina sono partiti proprio da un’analisi preliminare dell’impatto che possono avere nelle procedure ospedaliere, nella ridistribuzione dei compiti e delle risorse. Dato l’impiego digitalizzazione di che tecnologie come avanzate diffusione sia come dell’informazione, quindi nei settori dell’informatica e della telecomunicazione, è necessaria anche l’addestramento dello la formazione stesso del all’utilizzo personale corretto e delle infrastrutture e alla manutenzione di questo nuovi sistemi. A livello internazionale, ad oggi, si registra una mancanza di uniformità sull’applicazione del concetto di telemedicina, dato che vanno a sovrapporsi due differenti sfere di interesse, quella medica e quella ingegneristica; tutto ciò rende difficile la creazione di standard riconosciuti che possano regolamentare tale disciplina. Questo rappresenta un limite alla diffusione e all’impiego su larga scala delle soluzioni che in questi anni sono state elaborate. -7- CAP. 1 - TELEMEDICINA 1.3 STATO DELL’ARTE Oggigiorno i sistemi sanitari mondiali stanno facendo passi da gigante nell’utilizzo massiccio della telemedicina, grazie soprattutto allo sviluppo di nuove tecnologie. In ambito nazionale ogni regione si sta attrezzando allo sviluppo di sistemi di informatica medica. Uno dei rappresentato principali da progetti As.ter: un a livello nazionale sistema è informativo- informatico per la gestione di tutte le attività del territorio sviluppato dalla USL 11 di Empoli. As.Ter integra, con l’ausilio di una piattaforma tecnologica innovativa, tutti gli applicativi ed i database delle attività sanitarie e sociali svolte sul territorio, consentendo la rilevazione dei bisogni complessivi dei cittadini, l’individuazione delle modalità e dei tempi di risposta ai loro bisogni, la rilevazione dei costi; tutto ciò al fine di pianificare e gestire le attività sociosanitarie sul territorio in un ottica manageriale di costi/benefici. Un altro progetto è stato proposto dal servizio sanitario regionale Emilia-Romagna con il nome di SOLE. Esso è finalizzato a realizzare una rete telematica, basata su una serie di hub (centri di archiviazione) e spoke (centri di acquisizione), di collegamento tra i servizi ospedalieri e i servizi territoriali per agevolare la comunicazione tra operatori sanitari e, di conseguenza, agevolare l'erogazione -8- CAP. 1 - TELEMEDICINA dei servizi con importanti e positive ricadute sulla continuità assistenziale e sulla semplificazione dell’accesso ai servizi per il cittadino. Un’interessante applicazione sviluppata da un’equipe guidata da Ennio Amori, all’interno di questo progetto è “Neurosurgery Teleconsulting”, un servizio di teleconsultazione tra i centri di neurochirurgia collegati attraverso la rete hub&spoke, nella nuova logica distribuita e digitale, in modo da poter trattare ogni trauma e lesione in modo appropriato sfruttando il livello avanzato che ha raggiunto l’IT. Molti sono i progetti pilota che puntano a creare tra i nostri ospedali e centri d’eccellenza un collegamento con i paesi dell’Europa dell’est, che sono da poco entrati nella comunità europea, per fornire servizi di teleconsulto e telediagnosi, come per esempio tra Padova o Milano e la Romania. Nello sviluppo di questo tipo di progetti è possibile usufruire anche dei finanziamenti della comunità europea che ovviamente danno maggiore spinta alla ricerca e permettono di pagare la consulenza di software house, esterne alle aziende sanitarie, che possono dare un forte supporto tecnico e far ottenere un prodotto software più completo ed evoluto. In tutto questo panorama di progetti non emerge nessun sistema simile a quello sviluppato nella nostra tesi, si tratta sempre di servizi ad hoc per applicazioni specifiche, senza una vera ottica di integrazione e supporto globale di servizi sanitari delocalizzati. -9- CAP. 1 - TELEMEDICINA 1.4 PROGETTI DELL’ASUR MARCHE ZONA 7 L’ASUR della regione Marche zona 7 ha sviluppato una serie di progetti che riguardano l’ambito medico tra i quali troviamo: • Sistema di Teleconsulto ospedale di Osimo con stazioni fisse e stazioni mobili • Assistente Virtuale • Delocalizzazione della refertazione diagnostica 1.4.1 Sistema Osimo di con teleconsulto stazioni ospedale fisse e di stazioni mobili La Telelettrocardiografia è stata la prima applicazione derivata dalle esperienze aerospaziali sia americane che russe. Lo sviluppo enorme in tutti i paesi industrializzati della telefonia cellulare consente di erogare questo tipo di servizio di Telerefertazione con una eccezionale capillarità territoriale. Il sistema è composto da un server situato presso l’Ospedale di Osimo, le caratteristiche del server sono le seguenti: Server Compaq Proliant ML 370 T, P3 1266 GB, - 10 - CAP. 1 - TELEMEDICINA Cache 256 K ram 256 MB, 3 Hard Disk da 18 GB, il software permette la connessione fino a 25 apparecchiature in rete Ethernet. È inoltre composto da un client situato presso l’Ospedale di Osimo, le caratteristiche del client sono: Compaq EVO D500 SFFP 4 – 1.5 GB Hard Disk da 20GB, schede di rete CD Rom 48x, monitor 17” e stampante laser HP 1200 e da tre stazioni fisse composte da tre elettrocardiografi del tipo Archimed Base Plus, il quale consiste in un elettrocardiografo portatile per l’acquisizione contemporanea di dodici derivazioni dotate di monitor Lcd retroilluminato ad alta risoluzione di grandi dimensioni per la visualizzazione di sei tracce Ecg, tastiera alfanumerica, Driver per floppy da 3,5” e hard disk da 1 GB per la memorizzazione dei tracciati. L’elettrocardiografo è provvisto di varie uscite esterne, tra cui quattro seriali RS 232, una rete Ethernet per trasmissione e ricezione di tracciati ecg con relativo referto e stampante Archimed 4240 termica formato A4. Inoltre di una stazione mobile composta da una valigetta AT 4 Gsm che è una 24 ore completa di elettrocardiografo portatile computerizzata, dotato di Display Lcd di grandi dimensioni che permette di visualizzare contemporaneamente tre tracce ecg oltre una stampante per l’acquisizione contemporanea di dodici derivazioni ecg in modalità automatica, l’apparecchio è dotato di memoria interna per l’archiviazione di quaranta ecg (10 secondi 12 derivazioni) fornita di cellulare Gsm - 11 - CAP. 1 - TELEMEDICINA Dual Band con interfaccia per elettrocardiografo e cavi di connessione per la trasmissione digitale dell’ecg alla Centrale di Ascolto, funzione di viva voce per comunicare con la Centrale di Ascolto. Il Client è posto nella zona di controllo della Unità di Terapia Critica del Dipartimento di Medicina Interna ove un operatore è costantemente presente durante le ore del giorno e della notte. Le tre stazioni fisse sono al momento situate presso le Unità di degenza mediche del Presidio Ospedaliero di Osimo ed una presso i Poliambulatori di Camerano situati all’interno di una struttura residenziale per anziani con circa 80 degenti. La stazione mobile viene al momento affidata agli operatori della Emergenza Urgenza Territoriale che fanno capo al Distretto Ancona Sud. Fig. 1.2 – Applicazione di telemedicina con elettrocardiografo - 12 - CAP. 1 - TELEMEDICINA 1.4.2 Assistente Virtuale È un progetto ancora in fase embrionale che ha come obiettivo quello di creare un assistente personale virtuale che sia in grado di offrire ad ogni singolo utente un valido appoggio in riferimento alla propria situazione clinica. L’utente che si connetterà a questo nuovo sistema avrà a disposizione un alter-ego virtuale che attraverso un’interfaccia user-friendly gli permetterà di interagire in modo guidato con i suoi dati clinici, oltre che fornire in modo automatico un promemoria degli eventuali esami da svolgere o dei referti pronti alla consultazione. Quindi un sistema con interfaccia amichevole per accedere ai propri dati clinici che funga anche da archivio e agenda sanitaria. Fig. 1.3 – Interfaccia dell’assistente virtuale - 13 - CAP. 1 - TELEMEDICINA 1.4.3 Delocalizzazione della Refertazione Diagnostica Nell’ottica di capillarizzare l’offerta sanitaria così come negli obiettivi della sanità regionale, può essere importante pensare ad indipendenti una le struttura due fasi informatica tipiche della che renda diagnostica: l’esecuzione dell’esame e la sua refertazione. Infatti mentre è possibile pensare di localizzare alcuni macchinari (di basso costo) anche nei poliambulatori più piccoli, può risultare sconveniente portare in questi poliambulatori personale di alta specializzazione. Quello che ci si propone è di potenziare la rete di acquisizione dell’esame digitale, aumentando il numero delle macchine e dei tecnici di laboratorio, concentrando invece il personale medico che deve compilare il referto; il personale di laboratorio ha un costo inferiore rispetto ai dottori, quindi si aumentano capacità, capillarità e efficienza del servizio limitando gli investimenti. La struttura informatica che ci si propone di realizzare in questa fase, poi potrà anche servire per altri scopi come - 14 - CAP. 1 - TELEMEDICINA consulti medici oppure per fornire servizi (a pagamento) di refertazione per altri enti. L’architettura di massima proposta e quindi la seguente: Fig. 1.4 – Schema a blocchi delocalizzazione diagnostica Sostanzialmente un sistema per la refertazione a distanza altro non è che un repository di eventi sanitari con relativo output dell’esame (per esempio radiografia, elettrocardiogramma, etc). In questo repository gli enti periferici (poliambulatori, etc.) depositano gli output degli esami e prelevano i referti mentre il centro di refertazione preleva gli output degli esami ed inserisce i referti relativi. - 15 - CAP. 1 - TELEMEDICINA Per mettere in piedi la procedura appena descritta è indispensabile: che tutti i partecipanti siano collegati ad una rete intranet o internet; essere collegati ad una infrastruttura PKI funzionante; utilizzare un metodo di trasferimento sicuro (SSL). Il nostro progetto di tesi nasce proprio come ricerca di una soluzione ingegneristica a livello software per rispondere ad un’esigenza sanitaria di delocalizzazione. Requisiti fondamentali di tale sistema, data la varietà di dati da trattare e di ambienti in cui operare, sono la semplicità e la flessibilità; tenendo presenti questi due punti il sistema può raggiungere un livello di efficienza adeguato al supporto di servizi di teleconsulto e la telerefertazione e non solo, in generale per qualsiasi servizio sanitario delocalizzato. - 16 - CAP. 2 – SPECIFICHE PROGETTO MIRO CAPITOLO 2 SPECIFICHE PROGETTO MIRO Descrizione spiegazione dell’architettura del del funzionamento Progetto delle MiRo, varie componenti del sistema: repository, laboratorio e medico. Panoramica delle soluzioni adottate. 2.1 Finalità 2.2 Architettura Generale 2.2.1 Repository 2.2.2 Laboratorio 2.2.3 Medico 2.3 Soluzioni Tecniche 2.3.1 Comunicazione sicura e Mutuo Riconoscimento 2.3.2 Messaggi SOAP - 17 - tecniche CAP. 2 – SPECIFICHE PROGETTO MIRO 2.1 FINALITÀ Il progetto MiRo nasce dalla collaborazione tra l’Università Politecnica delle Marche con l’azienda ASUR Marche Zona 7 di Ancona, come soluzione software all’esigenza mostrata dall’ASUR, data la frammentarietà del nostro territorio marchigiano, di poter delocalizzare la refertazione diagnostica. L’obiettivo del nostro progetto era quello fornire all’azienda sanitaria locale, un prodotto flessibile che implementasse un servizio di refertazione asincrono; cioè in grado di separare non solo da un punto di vista temporale, ma anche da quello spaziale, l’esecuzione dell’esame dalla refertazione dello stesso. Per asincrono intendiamo che non ci si aspetta che l’esame venga refertato in tempo reale, ma in un tempo accettabile concordato tra i fornitori del servizio e gli utenti. Fig. 2.1 – Contesto del progetto - 18 - CAP. 2 – SPECIFICHE PROGETTO MIRO Come si evince dalla figura, la nostra architettura deve fornire un supporto per lo sviluppo di servizi sanitari, non si pone l’obiettivo di sostituire i sistemi informativi già esistenti che sono molteplici e variegati, ma li vuole integrare in una rete di comunicazione sicura che faciliti e razionalizzi lo scambio di informazioni. Nello specifico si tratta di servizi di teleconsulto in ambiente Intra/Internet, quindi basati su TCP/IP, come la refertazione e la “second opinion”. Visto che l’ambiente in cui opera il nostro sistema è intrinsecamente non sicuro, uno dei requisiti fondamentali da rispettare è il livello di sicurezza e di affidabilità, del trattamento del dato clinico, cioè consentire uno scambio di informazioni sicuro in un ambiente che non lo è. Lo scopo di MiRo è stato creare un’applicazione software completa in grado di telerefertazione e supportare modo in di gestire l’intero teleconsulto, sicuro delocalizzato. - 19 - un più processo di in generale servizio sanitario CAP. 2 – SPECIFICHE PROGETTO MIRO 2.2 ARCHITETTURA GENERALE Il primo passo fatto per progettare MiRo (che è l’acronimo per Medical Instruments for Report Objects) è stato capire come si doveva integrare con il sistema informativo sanitario semplificativa già adottata è esistente; stata la quella prima di ipotesi creare una piattaforma flessibile per lo sviluppo di servizi sanitari che avesse un’architettura orientata ai servizi. Si è pensato di creare una struttura semplice, basata su standard diffusi e riconosciuti, come Internet, in modo da sviluppare una piattaforma che potesse facilmente essere applicata in ambito sanitario sia a livello del personale medico che in un prossimo futuro direttamente ai cittadini. MiRo si basa sul concetto di evento, questo aspetto rappresenta L’effettuazione il punto di un focale esame dell’intero presso un sistema. laboratorio (qualsiasi, di una qualunque struttura ospedaliera) e la conseguente archiviazione del dato in forma digitale genera ciò che viene chiamato evento. L’evento non è il dato clinico vero e proprio ma rappresenta una sorta di meta-dato-clinico del dato digitale generato dai laboratori. È composto da una serie di - 20 - CAP. 2 – SPECIFICHE PROGETTO MIRO informazioni che riguardano il dato digitale prodotto dall’evento come ad esempio: l’unità che ha erogato la prestazione, la data e l’ora dell’esame, la struttura che l’ha prodotto, l’eventuale dottore richiedente ed il codice dell’impegnativa, lo stato della fase di refertazione, e, cosa molto importante il link al servizio web che consente di scaricare il dato, immagazzinato ecc…. all’interno Ogni di un evento quindi apposito viene raccoglitore, definito repository, che costituisce il nucleo del sistema e che funziona da collegamento tra colui che referta e l’esame. Questo legame è contenuto nell’evento e il repository è una sorta di sofisticato gestore di eventi clinici. Qualsiasi esame che può essere memorizzato in forma digitale può essere associato ad un evento, con questa soluzione ingegneristica siamo riusciti ad ottenere quella flessibilità ed adattabilità che venivano richieste in fase di progettazione. Una soluzione semplice, ma molto efficace, che abbiamo ritrovato anche in altri progetti di sistemi informativi in ambito sanitario. L’architettura, di conseguenza, mostra un’analoga flessibilità e semplicità, infatti è prevista la presenza solamente di tre attori principali che interagiscono tra loro e che rappresentano un’astrazione delle funzioni principali: da un lato l’esecuzione dell’esame, dall’altro la generazione del referto, con al centro un nodo di gestione del sistema. - 21 - CAP. 2 – SPECIFICHE PROGETTO MIRO Fig. 2.2 – Architettura generale di MiRO Il cuore del sistema è rappresentato dal Repository centrale che può essere ospitato da un azienda o da un ente erogante (in prima istanza l’ASUR stessa), il quale si impegna nel corretto funzionamento dell’intero sistema di refertazione e nella manutenzione dello stesso. Nella parte destra della figura sono rappresentate le strutture che producono i dati digitali di un esame clinico e che si interfacciano nel nostro sistema come dei client di laboratorio: sono quelli che attivano il processo di refertazione e attendono i risultati. Dall’altra parte si trova il personale che fornisce il servizio di refertazione, una clinica specializzata, un semplice medico o una equipe di medici; attraverso l’interfaccia del medico possono interagire nel nostro - 22 - CAP. 2 – SPECIFICHE PROGETTO MIRO sistema e pubblicare il proprio referto autenticandolo con la propria firma digitale. 2.2.1 Repository La struttura del sistema prevede la presenza di un repository centrale, costituito da un server che ospita un database relazionale nel quale vengono memorizzati le informazioni relative ai dati digitali prodotti nei laboratori. Ogni registrazione all’interno del database di questo repository costituisce quello che noi chiamiamo “evento”; l’evento rappresenta una sorta di meta-dato informativo riguardante il dato digitale prodotto nei laboratori. Le informazioni che vengono registrate sono data e ora dell’inserimento nella base dati, il tipo do esame eseguito e il laboratorio in cui è stato effettuato, informazioni sulla provenienza della richiesta di prestazione sanitaria e il percorso (pathname) dove recuperare l’esame o meglio il dato digitale riguardante l’esame vero e proprio. La scelta delle informazioni da memorizzare è stata fatta consultando il personale dell’azienda sanitaria, in modo da render semplici ed accessibili le informazioni principali riguardanti un esame. Da notare che non vengono mai registrate le informazioni relative al paziente, infatti il nostro primo obiettivo è quello di fornire un servizio di refertazione che - 23 - CAP. 2 – SPECIFICHE PROGETTO MIRO abbia la possibilità di concedere “second opinion”. Viceversa non viene fornito un servizio di diagnosi, per il quale sarebbe necessario un trattamento dei dati sensibili del paziente. Tutto ciò rende molto flessibile la struttura eliminando i problemi riguardanti la privacy. Nel caso di applicazioni future per telediagnosi, o per la consultazione della cartella clinica dei pazienti, avremmo l’obbligo legale di strutturare in modo più robusto, dal punto di vista della sicurezza, il server che ospita il repository, oltre all’utilizzo di tecniche di criptazione per la memorizzazione dei dati. 2.2.2 Laboratorio La seconda figura che viene presa in esame è il laboratorio, con questo termine intendiamo qualsiasi struttura sanitaria abbia necessità di farsi refertare un esame; potrebbe essere un laboratorio radiologico, analisi, epidemiologico oppure un autoambulanza con apposita apparecchiatura, una piattaforma petrolifera (anche essa con la giusta descrizione del strumentazione), nostro sistema ecc…. Quindi parleremo nella sempre di laboratorio come interfaccia comune che identifica in modo univoco ognuna di queste unità. Il laboratorio apre un evento, cioè una volta effettuato un esame e ottenuto il dato digitale sia in maniera diretta o tramite passaggi di discretizzazione, si collega al web - 24 - CAP. 2 – SPECIFICHE PROGETTO MIRO service dell’ente che fornisce il servizio di telerefertazione (nel nostro caso ASUR Marche zona 7) e tramite un opportuna interfaccia inserisce i dati relativi all’esame eseguito all’interno del database centrale. Grazie al repository che pubblica l’evento, il dato digitale presente nel laboratorio diventa accessibile e pronto per essere refertato da parte degli altri attori di questo sistema, i dottori o meglio il personale specializzato per la refertazione. Una volta generato l’evento il laboratorio può monitorare l’evento, ossia controlla tutte le varie fasi di refertazione che l’evento da lui generato subisce, fino a che non ritiene soddisfatta la sua richiesta. A questo punto il laboratorio dichiara chiuso l’evento e termina la fase di refertazione; logicamente solo chi ha generato la richiesta può dichiarare terminato il processo di refertazione. Fig. 2.3 – Funzionalità lato del laboratorio. - 25 - CAP. 2 – SPECIFICHE PROGETTO MIRO 2.2.3 Medico Una volta che il laboratorio apre l’evento e quindi va ad aggiungere un record al Repository centrale, effettua una richiesta di refertazione all’altra figura che compare nell’architettura di MiRo: i medici o per meglio dire il personale specializzato. Ogni medico effettuando l’accesso tramite un semplice web browser (per esempio Internet Explorer) accede al web service centrale dove tramite opportune interfacce userfriendly vengono mostrati solo gli eventi che ogni medico è in grado di refertare: questo perché in generale noi stiamo parlando di specialisti in particolari discipline mediche e non ha senso confondere l’utente con una serie di eventi che non ha nessuna utilità di consultare. Ad ogni dottore verrà visualizzato un elenco di tutti gli eventi che può refertare e dove sono presenti data e ora di inserzione nel database, il laboratorio che l’ha creata, lo stato dell’esame (aperto, refertato, chiuso) e infine il download ossia la possibilità di scaricarsi il dato ottenuto nel laboratorio sul proprio pc per visualizzarlo in modalità offline e quindi refertarlo. - 26 - CAP. 2 – SPECIFICHE PROGETTO MIRO Fig. 2.4 – Funzionalità lato del medico. Quando il medico decide di refertare un esame gli verrà presentata una form dove può scrivere il suo referto e contemporaneamente può leggere i referti emessi dagli altri medici, in questo modo si viene a creare un meccanismo definito “second opinion” (uno degli obiettivi di partenza del nostro lavoro). Un altro aspetto fondamentale che riguarda il medico è la firma digitale. Una volta scritto il referto il medico deve, tramite certificato digitale firmare il referto in modo da garantire a tutti gli operatori del sistema la propria identità e veridicità. Ciò se basta a confermare chi ha redatto il referto non da certezze su quando il referto sia stato scritto e firmato; quindi una volta scritto, il referto firmato deve essere spedito ad un web service esterno chiamato TimeStamp Service il quale sancirà in modo inequivocabile l’istante temporale in cui il dottore ha sottomesso il referto al sistema. Il web service TimeStamp rimanderà al medico il referto con il TimeStamp e il medico lo spedirà al repository centrale che lo immagazzinerà. - 27 - CAP. 2 – SPECIFICHE PROGETTO MIRO In questo modo si offrono le garanzie necessarie sulla fase di refertazione. Fig. 2.5 – Processo di Refertazione - 28 - CAP. 2 – SPECIFICHE PROGETTO MIRO 2.3 SOLUZIONI TECNICHE MiRo è un applicazione “object-oriented” basata su internet che fa uso di database relazionali per memorizzare i dati. Questa applicazione, basata su un modello client server viene eseguita all’interno di un web browser standard utilizzando la Java Virtual Machine, riducendo in maniera significativa i tempi e le difficoltà di configurazione dei client. Questo è un requisito fondamentale specialmente in ambiente remoto. MiRo è un sistema che si basa su di un’architettura orientata ai servizi, implementata attraverso Web Services, i quali stanno diventando uno standard “de facto” per quanto riguarda la distribuzione di servizi all’interno di un ambiente eterogeneo offrono un insieme come Internet. I Web Services di standard di comunicazione che permettono a diverse applicazioni di scambiarsi dati e servizi applicativi. Tutto ciò garantisce la massima accessibilità da parte degli operatori. L’architettura prescelta è quella delineata da Java 2 Standard Edition di Sun e gli strumenti di sviluppo sono: Eclipse 3.1 RC2 e JDK 1.5.0.5– come piattaforma di sviluppo; Apache Axis e Java Servlet – per la realizzazione dei web service; - 29 - CAP. 2 – SPECIFICHE PROGETTO MIRO Apache Struts e Java Sever Pages – per le interfacce web. Data la architettura così generica il sistema può operare su qualsiasi piattaforma che supporti i web services realizzati in Java. I database supportati sono tutti quelli con interfacce JDBC (nel nostro caso MySQL). I client possono accedere in qualsiasi momento per consultazione o inserimento dati utilizzando un comune Web Browser. Ogni operatore accederà a funzionalità diverse del sistema sulla base del proprio profilo professionale. 2.3.1 Comunicazione sicura e Mutuo Riconoscimento Un aspetto al quanto fondamentale, oggetto di studio e di progettazione nel nostro lavoro, è stata la sicurezza, nello specifico si è parlato di comunicazione sicura e mutuo riconoscimento. Anche se, come detto precedentemente, in questo lavoro non vengono mai trattati i dati sensibili relativi ai pazienti, la sicurezza delle comunicazioni all’interno del sistema svolge un ruolo fondamentale, perché la struttura è stata pensata e sviluppata per applicazioni che si basano su Internet, rete insicura per sua natura, si è reso necessario utilizzare il protocollo di - 30 - CAP. 2 – SPECIFICHE PROGETTO MIRO comunicazione https che rappresenta l’equivalente del classico http, dove le informazioni viaggiano in maniera criptata; le transazioni effettuate vengono criptate tramite lo standard SSL con mutuo riconoscimento. Fig. 2.6 – Comunicazione SSL con mutuo riconoscimento Alla base di una comunicazione sicura, è necessario un riconoscimento di conversazione. Tutto entrambi ciò è i stato partecipanti possibile alla attraverso l’utilizzo di certificati digitali che garantiscano l’identità di chi si collega al sistema. Una volta effettuato l’accesso e quindi, avvenuto il riconoscimento, al client (sia esso medico o laboratorio) viene offerto un accesso guidato all’interno della struttura in base alle informazioni che sono contenute nel certificato digitale; in questo modo il sistema può stabilire dal lato del laboratorio quali eventi può generare e controllare, viceversa dal lato del medico individua quali esami è abilitato a refertare. Il certificato digitale rappresenta una sorta di carta identità elettronica per chi si aggancia al sistema. - 31 - CAP. 2 – SPECIFICHE PROGETTO MIRO Tramite l’utilizzo della tecnologia SSL e dei certificati vengono soddisfatti i quattro requisiti fondamentali per la sicurezza: Autenticazione: garanzia che l’utente sia chi dichiara di essere. Ottenuta grazie al certificato ed agli Username e Password. Confidenzialità: garanzia che i dati possono essere letti esclusivamente dall’utente autorizzato. Ottenuta grazie ai certificati. Integrità: garanzia che le informazioni non vengano modificate durante la trasmissione. Ottenuta grazie alla crittografia. Non-ripudiazione: garanzia che chi utilizza il programma tramite il certificato se ne assuma le responsabilità. 2.3.2 Messaggi SOAP La comunicazione all’interno del sistema, avviene con scambio di messaggi SOAP (cosa sono e come funzionano verrà spiegato nei prossimi capitoli). I dati si inviano come allegati ai messaggi SOAP, abbiamo scelto questa tecnica per lo scambio di informazione perché avendo come obbiettivo quello di creare una struttura in grado di adattarsi a qualsiasi tipo di esame, era importante avere la possibilità di scambiare - 32 - CAP. 2 – SPECIFICHE PROGETTO MIRO messaggi di dimensioni variabili, in questo modo abbiamo raggiunto tale obbiettivo. Ad esempio, un dato proveniente da un laboratorio radiologico produce dei file Dicom di dimensioni che si aggirano attorno ai 40 Mb (dipende dall’esame), con questo sistema non si sono verificati problemi di trasmissione dei pacchetti. Una cosa importante da notare è che per implementare questa struttura si ha bisogno di una rete a larga banda che consenta le transizioni di file di dimensione variabile. - 33 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO CAPITOLO 3 IMPLEMENTAZIONE DEL PROGETTO Descrizione dell’implementazione del progetto, analisi degli elementi principali dell’architettura, dal repository centrale, ai web service del repository e del laboratorio. 3.1 Repository 3.1.1 Concetto di Evento 3.1.2 Struttura della base-dati 3.2 Web Service 3.2.1 Web Service Repository 3.2.2 Web Service Laboratory - 34 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO 3.1 REPOSITORY La struttura del sistema prevede la presenza di un repository, costituito da un database relazionale, nel quale vengono memorizzati i dati degli eventi generati dall’esecuzione di un esame presso uno dei laboratori collegati. All’interno dell’architettura che abbiamo studiato si possono distinguere differenti tipi di informazioni che vengono scambiati nella rete, uno dei quali è alla base del nostro sistema, l’evento, come è rappresentato in figura. Fig. 3.1 – Strutture dati astratte - 35 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO 3.1.1 Concetto di Evento L’evento all’interno corrisponde del alla repository. registrazione Tecnicamente dell’esame costituisce un record di una tabella all’interno di un database che risiede in un server centrale che costituisce il nucleo centrale dell’intero sistema. L’evento si configura come un metadato che racchiude le informazioni sul dato digitale vero e proprio, cioè sull’esame, e sul processo di refertazione. Usiamo il termine di meta-dato perché è un “dato del dato”: non contiene l’informazione originale, ma un puntatore ad essa. Per mezzo di questo link è possibile raggiungere il dato digitale dell’esame, corredato da ulteriori specifiche: chi ha eseguito l’esame e quando, oltre alla tipologia di prestazione. In questo modo l’evento astrae il dato digitale dell’esame, fornendo al sistema un modo univoco di accesso al dato, una specie di interfaccia per l’esame che astrae le varie differenze di formato dovuto alla varietà di periferiche digitale, quali elettrocardiografi o macchine memorizzati per radiografie. all’interno delle Questi memorie dati di rimangono massa dei laboratori, non andando a creare criticità per un possibile sottodimensionamento dei supporti rigidi all’interno del repository. Per esempio quando si parla di immagine DICOM, parliamo di file che possono raggiungere i 20 MB e considerando un bacino di utenza dell’ASUR di più di un milione di persone ci possiamo rendere conto che se - 36 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO fossero generate 50 immagini al giorno avremmo bisogno di 1GB al giorno. Inoltre andremmo a centralizzare la memorizzazione dei dati, in un’unica struttura, limitando l’indipendenza dei sistemi già presenti nelle varie zonalità. Volendo costituire un substrato che si integra con i sistemi informativi presenti non vogliamo sicuramente obbligare a determinate scelte organizzative per i vari enti, vogliamo che il costo di ingresso a questa rete di scambio di informazione sia il minimo, una semplice interfaccia di accesso al sistema e un servizio per la fornitura dei dati digitali memorizzati al proprio interno. In questo modo non è necessario apportare modifiche a procedure interne già esistenti, ma semplicemente aggiungere ulteriori funzionalità. Poi sarà l’ente stesso a valutare l’utilità di questo sistema di delocalizzazione delle risorse e dei servizi. In questa principalmente rete è evidente scambiata è che quella l’informazione dell’evento, che racchiude non solo l’esame come un link esterno, ma anche tutti i dati sulla refertazione. Oltre ad una struttura di memorizzazione relazionale come il database necessitiamo di un sistema di archiviazione su file-system, per registrare i referti. Il nostro repository deve avere una certa capacità di storage per poter archiviare sia i file di testo originali contenenti i referti, sia i file generati dal processo di firma. - 37 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO 3.1.2 Struttura della base-dati Fig. 3.2 – Diagramma della struttura della base-dati. Tab. 3.1 – Riepilogo delle tabelle. NOME TABELLA tm_eventi DESCRIZIONE Contiene gli eventi che vengono generati dai laboratori. - 38 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO Contiene la tipologia di esami che un tm_permissioni laboratorio è in grado di registrare nel sistema e quelli che un medico è in grado di refertare. tm_referti Contiene i referti relativi a ciascun esame registrato. Contiene le tipologie di esame, tm_tariffario codificate in un tariffario delle prestazioni. tm_unitaeroganti Contiene le unità che erogano i servizi sanitari. Contiene gli utenti registrati nel tm_utenti sistema, o come laboratori o come medici. Tab. 3.2 – Tabella degli Eventi. NOME CAMPO evt_id evt_trf_codice evt_codente DESCRIZIONE Identificativo univoco dell’evento all’interno del sistema. Tipologia dell’esame collegato all’evento. Codice identificativo dell’ente. evt_codstruttura Codice identificativo della struttura. evt_codspecialita Codice identificativo della specialità. - 39 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO evt_codunitaeroga Codice identificativo dell’unità nte erogante. Flag che indica se l’unità è esterna evt_esterno all’azienda sanitaria dove risiede il repository. evt_dtmevento Data e ora di apertura dell’evento. evt_dtmchiuso Data e ora di chiusura dell’evento. evt_token evt_wslink evt_stato evt_richiedente evt_impegnativa evt_note Identificativo dell’esame che permette di ottenere i dati collegati. Link al web service che deve fornire i dati digitali dell’esame. Stato dell’evento (0=aperto, 1=refertato, 2=chiuso). Informazione aggiuntiva del medico o struttura che ha richiesto l’esame. Informazione aggiuntiva del codice dell’impegnativa. Eventuali note legate all’evento. Tab. 3.3 – Tabella delle Permissioni. NOME CAMPO prm_utn_id DESCRIZIONE Identificativo univoco dell’utente all’interno del sistema. - 40 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO Tipologia dell’esame a cui l’utente prm_trf_codice può accedere, sia esso un laboratorio che un medico. Tab. 3.4 – Tabella dei Referti. NOME CAMPO ref_evt_id DESCRIZIONE Identificativo univoco dell’evento all’interno del sistema. Indice progressivo del referto dell’evento, in questo modo un ref_ind referto è individuato univocamente dal campo ref_evt_id e dal campo ref_ind. ref_filename ref_timestamp ref_notvalid Nome del file che contiene il referto in formato testo. Data e ora di sottomissione del referto. Flag che indica se il referto è stato invalidato. Identificativo univoco dell’utente ref_utn_id all’interno del sistema che ha scritto il referto. - 41 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO Tab. 3.5 – Tabella del Tariffario. NOME CAMPO trf_codice trf_descr DESCRIZIONE Codice identificativo della tipologia dell’esame. Descrizione per esteso della prestazione. Tab. 3.6 – Tabella delle Unità Eroganti. NOME CAMPO uer_codente DESCRIZIONE Codice identificativo dell’ente. uer_codstruttura Codice identificativo della struttura. uer_codspecialita Codice identificativo della specialità. uer_codunitaeroga Codice identificativo dell’unità nte uer_descr erogante. Descrizione per esteso dell’unità erogante. Tab. 3.7 – Tabella degli Utenti. NOME CAMPO utn_id utn_cn DESCRIZIONE Identificativo univoco dell’utente all’interno del sistema. CommonName che deve essere - 42 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO presente anche all’interno del certificato digitale. utn_type utn_codente Tipo di utente (L=laboratorio, M=medico). Codice identificativo dell’ente. utn_codstruttura Codice identificativo della struttura. utn_codspecialita Codice identificativo della specialità. utn_codunitaeroga Codice identificativo dell’unità nte erogante. Nel caso di laboratorio specifica il link utn_labwslink al web service per l’ottenimento dei dati di laboratorio. utn_archivepath Specifica la cartella di lavoro dell’utente. - 43 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO 3.2 WEB SERVICE 3.2.1 Web Service Repository Fig. 3.3 – Struttura del WebService Repository Il cuore del sistema è rappresentato dal Web Service Repository che offre una serie di funzioni che permettono la gestione del repository. In figura è evidenziata la struttura della classe con i metodi esposti (quelli nei riquadri di - 44 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO colore giallo), quelli che costituiscono l’interfaccia con cui è possibile comunicare con l’oggetto. Sarà poi questa interfaccia ad essere tradotta da codice Java in un file WSDL, ovvero il documento xml che descrive i metodi forniti da un web service. Questo file viene poi pubblicato e reso disponibile a chiunque voglia fruire di uno dei servizi web forniti, per visualizzarlo basta richiamare l’URL WSRepository?wsdl a partire dal percorso che contiene il web service. Fig. 3.4 – Visualizzazione del file WSDL descriptor Questo file è fondamentale nell’architettura distribuita, perchè rappresenta come è possibile comunicare e interagire con il server e quindi anche come costruire un - 45 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO client che lo possa sfruttare. In ambiente Java è presente un tool, Java2WSDL, che automatizza questo processo e si carica del tedioso lavoro di creazione del file descrittore WSDL a partire da una classe Java. Fig. 3.5 – Struttura del file WSDL Ad ogni metodo pubblico della classe corrisponderà un metodo del web service, richiamabile attraverso una URL in un formato particolare: WSRepository?method=”nome del metodo”&”nome del parametro”=”valore del parametro”&... Dall’altra parte, è presente un tool che compie il lavoro inverso: WSDL2Java, a partire dal descrittore WSDL crea le classi per una minima implementazione del client. Per maggiori informazioni su questi tool è possibile consultare la documentazione della Sun. Vediamo ora una panoramica dei metodi esposti dalla classe WSRepository e quindi dal corrispondente Web Service: - 46 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO closeevent(int evt_id) chiude un evento esistente, l’evento è individuato dal suo identificativo; echo(String value) funzione di test del webservice; ritorna la stringa che è stata inviata come parametro; getreports(int evt_id) ritorna l'elenco dei referti come una stringa e in allegato tutti i file di referto associati all’evento specificato; invalidatereport(int evt_id, int ref_ind) invalida un referto di un evento esistente, l’evento è individuato dal suo identificativo mentre il referto dal suo indice; listevents(int utn_id) ritorna l'elenco degli eventi in base alle permissioni dell’utente, l’utente è individuato dal suo identificativo; openevent(int utn_id, String evt_codice, boolean evt_esterno, String evt_token, String evt_wslink, String evt_richiedente, String String evt_note) - 47 - evt_impegnativa, CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO apertura dell'evento, con inserimento di un nuovo record nella tabella informazioni degli eventi passate come e registrazione parametro e di delle quelle ottenute in modo automatico; uploadreport(int evt_id, int utn_id, String evt_content) carica nel repository un nuovo referto per l'evento, viene inserito un nuovo record nella tabella dei referti, memorizzato il contenuto del referto in un file e salvata la firma del dell’integrità referto dei per dati, una questa successiva viene verifica passata come allegato; userinfo(String utn_cn) ritorna le informazioni dell'utente, individuato CommonName estratto dal certificato del client; - 48 - dal CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO 3.2.2 Web Service Laboratory Fig. 3.6 – Struttura del WebService Laboratory Visto che il dato digitale dell’esame rimane memorizzato all’interno del laboratorio, questo deve fornire un modo per ottenere questa informazione. Quindi anche il laboratorio ha bisogno di un web service. Il client che deve eseguire il referto di un esame, avrà la possibilità di visualizzare e consultare il dato richiamando il servizio web esposto dal laboratorio che ha generato l’evento corrispondente all’interno del repository. Ogni struttura che voglia partecipare alla nostra rete come laboratorio che produce eventi clinici, come requisiti, deve provvedersi di un servizio web per la condivisione dei propri dati sugli esami, mantenendoli però all’interno della propria struttura e conservando una certa autonomia. Il - 49 - CAP. 3 - IMPLEMENTAZIONE DEL PROGETTO laboratorio non deve cambiare le proprie procedure, ma deve solo integrare questa funzionalità di condivisione delle risorse. Vediamo ora una panoramica dei metodi esposti dalla classe WSLaboratory e quindi dal corrispondente Web Service: echo(String value) funzione di test del webservice; ritorna la stringa che è stata inviata come parametro; getevent(String file) ritorna come allega il file di risorsa contenente i dati digitali dell’esame e inviduati dal token passato; - 50 - CAP. 4 - TECNOLOGIE SOFTWARE CAPITOLO 4 TECNOLOGIE SOFTWARE Panoramica delle tecnologie software impiegate nella realizzazione del progetto dal lato server del repository con i web service. 4.1 Java Platform 4.1.1 Java lato server: le Servlet 4.2 Service-Oriented Architecture 4.2.1 Web Services 4.2.2 Protocollo SOAP 4.3 Application Server 4.3.1 Apache Tomcat 4.3.2 Comunicazione SSL 4.4 Strumenti di Sviluppo 4.4.1 Apache Axis 4.5 Database Relazionale 4.5.1 MySQL - 51 - CAP. 4 - TECNOLOGIE SOFTWARE 4.1 JAVA PLATFORM Fig. 4.1 – Architettura della piattaforma Java. Java appena è uscito è stato accolto con molto entusiasmo dalla comunità mondiale dei progettisti di software e dei provider di servizi Internet, questo perché Java permetteva agli utenti di Internet di utilizzare applicazioni sicure e indipendenti dalla piattaforma, che si possono trovare in qualsiasi punto della rete. Java è quindi nato come linguaggio per la rete, per affiancare l'Hyper Text Markup Language (HTML), il quale non è un linguaggio di programmazione vero e proprio, e per dargli quella sicurezza che l'HTML non ha. Da quando è - 52 - CAP. 4 - TECNOLOGIE SOFTWARE nato Java sulla rete si è iniziato a poter parlare di numeri di carte di credito e di informazioni sicure, notizia che ha molto affascinato le grosse società mondiali, le quali hanno trasformato la vecchia Internet, rete ad appannaggio delle sole università e centri di ricerca, nell'attuale mezzo di comunicazione aperto a tutti. Il linguaggio di programmazione Java è stato creato verso la metà degli anni novanta, è il più recente tra i suoi cugini, e per questo è ancora in fase evolutiva, tanto che ogni anno circa ne viene rilasciata una nuova relase. Da linguaggio nato solo per la rete è divenuto un vero e proprio linguaggio di programmazione, paragonabile, dal punto di vista delle funzionalità, al più blasonato C++. Java e la maggior parte degli altri linguaggi possono essere paragonati solo dal punto di vista delle funzionalità, perché sono fondamentalmente molto diversi, infatti Java compila i sorgenti dei suoi programmi in un codice detto Bytecode, diverso dal linguaggio della macchina su cui è compilato, mentre linguaggi come il C++ compilano i sorgenti dei programmi in un codice che è il codice della macchina ( per macchina intendo computer + sistema operativo ) su cui è eseguito. Quindi per eseguire un programma Java occorre avere uno strumento che è chiamato Java Virtual Machine, la quale interpreta il bytecode generato dal compilatore Java e lo esegue sulla macchina su cui è installato. Grazie alla Java Virtual Machine Java - 53 - è indipendente dalla CAP. 4 - TECNOLOGIE SOFTWARE piattaforma, infatti il programma compilato Java è legato alla JVM e non al sistema operativo, sarà quindi possibile eseguire lo stesso programma Java, compilato una sola volta su una qualche macchina con un compilatore Java versione X, su una piattaforma Windows e su una piattaforma Linux, per fare questo però c'è bisogno che sia Windows che Linux abbiano installato una Java Virtual Machine che supporti la versione X di Java. Le due JVM installate sulle due piattaforme diverse sono lo stesso programma compilato una volta per Windows ed una volta per Linux, come avveniva con i programmi scritti in linguaggi come il C/C++. Una Java Virtual Machine è implementata anche nei vari Browser (Come Netscape e Explorer) per poter eseguire i programmi Java incontrati nella rete, i cosidetti Applet. Questo però, unito al fatto che Java ancora si evolve, causa degli ovvi problemi di incompatibilità: capita sempre che il più moderno Browser supporti una versione precedente di Java rispetto all'ultima versione rilasciata dalla Sun Microsystem, inoltre bisogna tener presente che non tutti gli utenti di Internet navigano usando l'ultima versione di Netscape o di Explorer. Quindi volendo creare un applet ed inserirlo in un nostro documento HTML, dobbiamo tenere presente questi problemi, e cercare di scrivere un programma che sia compatibile con la maggior parte delle JVM inplementate nei vari browser. - 54 - CAP. 4 - TECNOLOGIE SOFTWARE Un altro problema da affrontare è quello della scelta del compilatore Java da utilizzare, infatti esistono vari ambienti integrati per editare, compilare, debuggare ed eseguire programmi Java, come quelli della Borland, della Microsoft, della Symantec. Tutti questi ambienti offrono dei tool di sviluppo eccellenti, come editori grafici di finestre, debugger molto interessanti, però hanno due problemi, il primo è che si pagano, anche molto, il secondo è sempre lo stesso della compatibilità, infatti essi spesso si trovano indietro alla relase della sun, ed inoltre aggiungono delle classi che poi le JVM implementate nei browser non hanno. Il consiglio è quello di usare le JDK (Java Development Kit) della Sun, le quali comprendono sia il compilatore che la Java Virtual Machine per eseguire i programmi da noi compilati, inoltre sono freeware (non costano niente) e sono scaricabili dalla rete ed i browser si adeguano pian piano a questa versione di Java. Se volete scrivere applet per i vecchi browser dovete scaricarvi la versione 1.1 di Java. Comunque ad oggi le ultime versioni dei programmi di navigazione più diffusi supportano la più recente release di Java, cioè la 1.5, quella che abbiamo utilizzato nello sviluppo del nostro progetto. Un ultimo problema che ha Java è la lentezza, infatti, come già detto, esso è interpretato, quindi le istruzioni Java - 55 - CAP. 4 - TECNOLOGIE SOFTWARE prima di essere eseguite dalla macchina vengono interpretate dalla JVM, ovvero per eseguire ogni istruzione il computer eseguirà un numero di istruzioni macchina che è più del doppio delle istruzioni che eseguirebbe se la stessa istruzione fosse stata scritta in C, quindi avrete bisogno di computer veloci per eseguire bene programmi Java, e di questo vi sarete sicuramente accorti anche navigando sulla rete. Per finire ritorniamo al Java Development Kit della Sun Microsystem, con questo è possibile produrre tutto il software che si vuole senza dover pagare diritti di uso del prodotto come avviene con il Borland Jbuilder, il Symantec Cafe, e il Microsoft Visual Java, vi consigliamo di leggere la licenza d'uso che troverete quando andrete a scaricare il JDK prima di cominciare a produrre software. La principale differenza tra Java e gli altri linguaggi di programmazione ad oggetti è che mentre con questi ultimi è possibile anche programmare ad oggetti con Java si deve assolutamente programmare ad oggetti. Quindi punto fondamentale a questo punto è spiegare cosa vuol dire programmare ad oggetti. Sostanzialmente la programmazione avviene allo stesso modo dei linguaggi "normali", solo che sia i dati che le funzioni che li manipolano sono racchiusi in delle strutture dette classi. - 56 - CAP. 4 - TECNOLOGIE SOFTWARE Le classi sono dei prototipi di oggetti, ovvero sono delle strutture astratte che possono essere istanziate e quindi creare un oggetto (ma anche più di uno). La classe definisce tutte le proprietà degli oggetti appartenenti a quella classe, detti attributi, e le funzioni che verranno usate per agire su di essi, detti metodi. Ad esempio è possibile definire una classe delle persone, come segue: Inizio classe persone Attributo annodinascita Metodo calcolaetà (annoattuale) Fine classe persone La classe delle persone così definita ha un attributo che è annodinascita che sarà sicuramente un numero intero ed un metodo che in base all'anno attuale passatogli calcola l'età della persona. Usando il formalismo di Java,per definire la classe persone scriveremo: class persone { public int annodinascita; public int calcolaeta ( int annoattuale ) { return ( annoattuale - annodinascita ); } - 57 - CAP. 4 - TECNOLOGIE SOFTWARE } Come si vede abbiamo dichiarato sia il metodo che l'attributo come public, vedremo tra poco cosa significa, vediamo anche che la classe comincia con { e finisce con }, così anche i metodi. Questo ricorda molto il C, e devo dire che la sintassi di Java è molto simile, anzi quasi uguale a quella del C, mentre per chi non conosce il C, le parentesi graffe rappresentano il begin e l'end del pascal. La classe avrà un cosiddetto costruttore (o più di uno), che è un metodo particolare che di solito viene utilizzato per inizializzare gli attributi quando viene instanziata la classe in un oggetto, esso è una funzione che non ha nessun tipo di ritorno ed il nome uguale al nome della classe. Ho detto che i costruttori possono essere più di uno, che però il nome del costruttore deve essere lo stesso di quello della classe, chi è abituato a programmare con linguaggi non orientati agli oggetti troverà tutto questo strano, però è possibile perché Java fa il cosidetto overloading di funzioni, ovvero funzioni con lo stesso nome che hanno parametri diversi (detti in informatica parametri formali) sono diverse, e al momento dell'invocazione viene scelta la funzione in base al parametro (detto parametro attuale). Questo vale per ogni metodo, non solo per i costruttori. - 58 - CAP. 4 - TECNOLOGIE SOFTWARE class persone { public int annodinascita; public String Cognome=new String(); // Costruttori public persone(int annonascita) { this("Non Conosco"); this.annodinascita=annonascita; } public persone(String Cognome) { this(0); this.Cognome=Cognome; } public persone(int annonascita , String Cognome) { annodinascita=annonascita; this.Cognome=Cognome; } // Funzione che calcola l'età del soggetto; public int calcolaeta ( int annoattuale ) { return ( annoattuale - annodinascita ); } - 59 - CAP. 4 - TECNOLOGIE SOFTWARE } Nell'esempio vediamo che ci sono tre costruttori, diversi per i parametri formali, che hanno lo stesso nome, vediamo inoltre un nuovo attributo che è Cognome, esso è una Stringa, definita come public String Cognome=new String(); la parte prima dell'uguale è chiara, lo è meno quella a destra, quel new String() crea un nuovo oggetto della classe String, e ne invoca il costruttore che non ha parametri, questo è il modo standard usato da Java per instanziare gli oggetti di una classe. Non deve sorprendere che il tipo di dato stringa sia una classe, in Java è possibile usare oggetti che rappresentano tutti i tipi di dato del linguaggio, inseriti per completezza del linguaggio, detti involucri che a volte sono molto utili, è però possibile anche usare i valori. Quindi ad esempio ci troveremo a lavorare sia con interi che con oggetti che rappresentano interi. Un ultima cosa che salta all'occhio dall'esempio è che i costruttori hanno volutamente dei parametri che hanno lo stesso nome degli attributi, anche questo è possibile in Java, il quale stabilisce che quando c'è un assegnamento alla sinistra dell'uguale ci deve essere l'attributo, e alla destra il parametro, comunque se non vogliamo confonderci possiamo usare il riferimento this, scrivendo ad esempio this.annodinascita intendiamo l'attributo. This è un riferimento all'oggetto, e nell'esempio lo troviamo anche - 60 - come invocazione di CAP. 4 - TECNOLOGIE SOFTWARE funzione this(0), in questo caso esso è un riferimento ad un costruttore dell'oggetto, in questo caso chiama il costruttore persone (int annodinascita), con il valore 0. È quindi possibile in un costruttore chiamare un costruttore diverso della classe stessa, a patto che l'invocazione sia la prima istruzione del costruttore e che il costruttore sia diverso da quello attuale. A questo punto siamo pronti a creare oggetti appartenenti alla classe da noi appena definita, abbiamo tre modi per farlo, perché abbiamo creato tre costruttori, essi sono: persone Pietro=new persone(1974); oppure persone Pietro=new persone("Castellucci"); oppure persone Pietro=new persone(1974,"Castellucci"); Introduciamo adesso degli attributi e dei metodi particolari, i cosidetti membri statici. Per come abbiamo definito i membri della classe non ci è possibile referenziare direttamente dalla classe attributi e metodi ( persone.annodinascita è un errore), questo perché essi lavorano su una istanza della classe, ovvero su un oggetto, però a volte può essere utile scrivere metodi e attributi che possano essere invocati senza dover istanziare l'oggetto, - 61 - CAP. 4 - TECNOLOGIE SOFTWARE ma direttamente dalla classe, per fare questo occorre dichiararli static, ad esempio: class TitoliAziendaVattelapesca { public static int TassodiInteresse=3; public String Proprietario=new String(); public static float InteressiMaturati (int Periodo) { return((Periodo * TassodiInteresse )/100) } TitoliAziendaVattelapesca(String nome) { Proprietario=nome; } } Introduciamo adesso la relazione is_a tra classi, data una classe è possibile creare una nuova classe da questa facendo come si dice in gergo una specializzazione della prima classe. La nuova classe creata è in relazione is_a con la prima. Creata una classe studente dalla classe (detta superclasse) persone, la nuova classe eredita dalla prima tutti i metodi e gli attributi, con la possibilità di definirne dei nuovo o di ridefinirne alcuni, in Java l'estensione di una classe si esplicita con la parola chiave extends. - 62 - CAP. 4 - TECNOLOGIE SOFTWARE class studente extends persone { int matricola; // Costruttori public studente(int annonascita) { super(annonascita,"Non Conosco"); } public studente (String Cognome) { super(0,Cognome); } public studente(int annonascita , String Cognome) { super(annonascita,Cognome); } } Come si vede dall'esempio la classe studente eredita tutti i metodi e gli attrubuti della classe persone, definisce un nuovo attributo matricola e nei suoi costruttori chiama i costruttori della classe persone, con super(). - 63 - CAP. 4 - TECNOLOGIE SOFTWARE super() può essere, come this(), una invocazione di un altro costruttore (tra parentesi vanno i parametri eventuali) o un riferimento alla classe (alla superclasse in questo caso), quindi super.annodinascita rappresenta l'attributo annodinascita della superclasse persone. Le relazioni is_a e inst_of sono le due relazioni più importanti dei modelli ad oggetti. I package sono delle collezioni di classi, racchiuse in una collezione che le accomuna. Sono in pratica delle librerie a cui l'utente può accedere e che offrono varie funzionalità. I package possono anche essere creati dall'utente, ad esempio racchiudendovi tutte le classi che ha definito per svolgere alcune funzioni che poi userà in vari programmi, ma questo a noi non interessa, perché ci interesserà vedere i package sicuramente più interessanti definiti in Java. È questa la vera potenza odierna di Java, il numero di classi già definite che svolgono i più svariati compiti, ed è anche la parte che continua sempre di più a crescere e ad aggiornarsi con le nuove versioni di Java (JDK 1.3 ne ha ben 18Mb). Il nucleo del linguaggio Java contiene solo le parole chiave per la costruzione delle classi, i commenti, i normali costrutti if, switch, while, do-while, for, etichette, break, continue e return (manca il goto), tutto il resto è implementato nei package del linguaggio, comprese le - 64 - CAP. 4 - TECNOLOGIE SOFTWARE normali primitive di Input e di Output. In questo paragrafo vedremo la lista dei packages di Java e ne vedremo uno in particolare, quello che ci interessa vedere per scrivere la nostra prima applicazione Java ancor prima di avere visto i costrutti, ovvero quello in cui possiamo trovare le istruzioni di input e output. Sembrano pochini in effetti, sembra quasi che io abbia detto un stupidaggine in riferimento alla potenza di Java, ma se pensate che solo il package java.io comprende 50 classi e 10 interfacce capite bene che quelli di sopra sono una collezione di classi corposa. A questo punto vorremmo fare l'input e output da console, per fare questo dobbiamo usare il package java.lang . Per usare un package in una nostra classe, prima della definizione della classe dobbiamo inserire l'istruzione import, ad esempio volendo usare il package java.awt dobbiamo inserire all'inizio del nostro file: import java.awt.*; La * sta ad indicare che vogliamo usarne tutte le classi, se invece vogliamo usarne solo una classe possiamo specificarlo, ad esempio, import java.awt.Frame; potremo usare solo la classe Frame dell'awt. Nel caso nostro, in cui - 65 - CAP. 4 - TECNOLOGIE SOFTWARE vogliamo fare una operazione di output, dovremmo dichiarare all'inizio import java.lang.*; oppure, sapendo che la classe del package java.lang che contiene i metodi per fare questo è System potremmo scrivere import java.lang.System; La classe System a sua volta avrà al suo interno una import java.io (che è il package per l'input e l'output) per accedere alle classi dell'input e dell'output, e le userà per mandare quello che vogliamo su schermo. Abbiamo visto un esempio di package che al suo interno invoca un altro package per fargli svolgere dei compiti, in queste librerie Java accade spesso questo, ed è proprio questo fenomeno che a molti fa apparire Java un linguaggio ostico, però superato questo blocco psicologico di fronte a quest'aspetto del linguaggio, la programmazione diviene semplice ed immediata. Abbiamo introdotto il package java.lang, vi dico anche che questo è il più importante di Java, in questo sono racchiuse le classi fondamentali del linguaggio, tanto che non serve dichiarare l'import, perché java lo importa automaticamente. Inoltre bisogna soffermarci su un aspetto fondamentale dell'importare i package, se noi importiamo nel mio file il package java.lang, anche se esso importerà il - 66 - CAP. 4 - TECNOLOGIE SOFTWARE package java.io, io dal mio file non potremmo usare le classi di java.io, per farlo devo importarlo esplicitamente. Questo succede anche se programmiamo una applicazione in più file (con più classi), in ogni file dobbiamo importare i package che ci occorrono per la classe definendo, non basta importarli in una sola. - 67 - che stiamo CAP. 4 - TECNOLOGIE SOFTWARE 4.1.1 Java lato server: le Servlet La crescente richiesta di pagine web con contenuti dinamici e di servizi su web, ha portato allo sviluppo di una particolare tipologia di software che collabora con il server web per estenderne le funzionalità e poter interagire con basi di dati. Le tecniche per realizzare questo tipo di software, sono tante, tra cui le più conosciute sono sicuramente le estensioni CGI (Common Gateway Interface), realizzate principalmente in C o in Perl. Anche Java mette a disposizione una API, le Servlet, appunto, che permette di sviluppare applicazioni lato server. Daremo una descrizione di tipo generale su questa tecnologia che si è rivelata molto migliore delle tecniche precedenti sotto molti aspetti quali la portabilità e l'efficienza. Fig. 4.2 – Java based Web Application Technology. - 68 - CAP. 4 - TECNOLOGIE SOFTWARE Che cos'è una Servlet? Le Servlet sono moduli software scritti in Java che vengono eseguiti in applicazioni lato server per esaudire le richieste dei client. Esse non sono legate ad un particolare protocollo per la comunicazione tra client e server, anche se più comunemente si utilizza il protocollo HTTP ed infatti si parla di http Servlet. Per scrivere una Servlet si fa uso delle classi del package javax.servlet che è il framework di base per la scrittura delle servlet e del package javax.servlet.http che è l'estensione del framework di base, per la comunicazione via http. Utilizzando un linguaggio portabile come Java, le Servlet consentono di realizzare soluzioni per estendere le funzionalità dei server web, indipendenti dal sistema operativo su cui esse vengono eseguite. Per cosa vengono utilizzate? Le Servlet vengono più comunemente utilizzate per: elaborare e salvare dati provenienti da form HTML; provvedere alla creazione di pagine HTML dinamiche, ad esempio utilizzando i dati contenuti in un database, in seguito ad una richiesta di un client; gestire informazioni con stato, ad esempio la gestione di un sistema di commercio elettronico, dove un certo numero di clienti fa degli acquisti contemporaneamente e dove occorre quindi mantenere in modo corretto le - 69 - CAP. 4 - TECNOLOGIE SOFTWARE informazioni sui clienti e sui loro acquisti (il classico carrello della spesa); Servlet vs CGI Le Servlet hanno diversi vantaggi rispetto al tradizionale CGI e precisamente sono più efficienti, più facili da utilizzare, più potenti, più portabili e più economici. Vediamo più in dettaglio ciascun punto: Efficienza. Con una tradizionale applicazione CGI, viene generato un processo (istanza del programma o script CGI) per ogni richiesta che arriva al server, questo tipo di operazione può risultare abbastanza pesante in termini di risorse. Con le Servlet invece, la JVM (Java Virtual Machine) genera, per ogni richiesta da esaudire, un Thread Java, molto più leggero (lightweight) di un processo generato dal sistema operativo: con il CGI se ci sono N richieste contemporanee, esistono N immagini in memoria del programma CGI; con le Servlet invece, ci sono N Thread ma una sola copia della classe Servlet in memoria. Inoltre c'è da dire che l'efficienza è ancora superiore se si pensa che, ad esempio, un'applicazione CGI che si interfaccia con un database si connette ad esso ad ogni richiesta che arriva e si disconnette al termine: operazioni queste molto pesanti. Con le Servlet invece è possibile mantenere - 70 - aperte una o più CAP. 4 - TECNOLOGIE SOFTWARE connessioni al database, utilizzando ad esempio una Connection Pool, e riutilizzarle da richiesta a richiesta. Facilità d'uso. Java mette a disposizione una API per la scrittura delle servlet che facilità molto le varie operazioni coinvolte nell'utilizzo di una Servlet e cioè: manipolazione dei dati provenienti dai form HTML, lettura e gestione delle intestazioni (headers) delle richieste http, gestione dei Cookie e delle sessioni e molte altre cose utili. Potenza. Con le servlet si possono fare tante cose che con i programmi CGI sono difficili o addirittura impossibili da realizzare: le servlet possono dialogare direttamente con il server web facilitando l'utilizzo e la condivisione di dati (testi, immagini ecc.) . Inoltre le servlet facilitano operazioni come la session tracking, permettendo il mantenimento di informazioni tra una richiesta e un'altra, e di caching delle operazioni già eseguite in una richiesta precedente. Portabilità. Essendo scritte in Java mediate un'apposita API standard, le Servlet possono girare su qualsiasi piattaforma. Oggi le servlet sono supportate, direttamente o mediante plug-ins (servlet engines), dalla maggior parte dei server commerciali e free. - 71 - web in circolazione, CAP. 4 - TECNOLOGIE SOFTWARE Convenienza. Molti server web che supportano le servlet sono free o comunque hanno un basso costo. Se si dispone già di un server web commerciale (di quelli cari!) che non supporta le servlet l'aggiornamento con appositi plug-ins per il supporto delle servlet è normalmente free. Architettura di base e ciclo di vita di una Servlet Una Servlet, nella sua forma più generale, è un'istanza della classe che implementa l'interfaccia javax.servlet.Servlet, e cioè javax.servlet.GenericServlet; ma come abbiamo già detto quella più utilizzata è quella che usa il protocollo HTTP e che si costruisce estendendo la classe javax.servlet.http.HttpServlet. Vediamo adesso il tipico ciclo di vita di una Servlet. All'avvio, il server carica in memoria la classe Servlet ed eventualmente le classi utilizzate da questa, e crea un'istanza chiamando il costruttore senza argomenti. Subito dopo viene chiamato il metodo init(ServletConfig config). Qui avvengono le inizializzazioni delle variabili globali, procedura che viene fatta una sola volta durante l'intero ciclo di vita della Servlet, e viene caricato l'oggetto ServletConfigche potrà poi essere recuperato più tardi mediante il metodo getServletConfig(). Quest'ultima operazione viene eseguita nel metodo init della classe GenericServlet ed è per questo che in ogni classe che la - 72 - CAP. 4 - TECNOLOGIE SOFTWARE estende, come lo è HttpServlet, all'inizio del metodo init c'è una chiamata al super.init(config). metodo L'oggetto della superclasse ServletConfig e cioè contiene i parametri della Servlet e il riferimento a ServletContext che rappresenta il contesto in cui gira la Servlet. Una volta inizializzata la Servlet, viene chiamato il metodo service (ServletRequest req, ServletResponse res) per ogni richiesta che arriva dai client; questo metodo viene chiamato in modo concorrente cioè più Thread possono invocarlo nello stesso momento. In casi particolari e cioè quando si lavora con risorse non condivisibili, è possibile implementare Servlet non concorrenti. Quando una Servlet deve essere arrestata, ad esempio se deve essere aggiornata o bisogna riavviare il server, viene chiamato il metodo destroy() nel quale vengono rilasciate le risorse allocate nel metodo init. Anche questo metodo viene chiamato una sola volta durante il ciclo di vita della Servlet. Conclusioni Possiamo concludere dicendo che, le Servlet sono uno strumento tra i più validi per le applicazioni che richiedono contenuti web dinamici; sono abbastanza semplici da sviluppare e consentono di eseguire operazioni che con le tecniche precedenti risultavano difficili, questo grazie alla possibilità di utilizzare tutte le API che Java mette a disposizione, che sappiamo essere numerose e varie. - 73 - CAP. 4 - TECNOLOGIE SOFTWARE 4.2 SERVICE-ORIENTED ARCHITECTURE Durante l’evoluzione della tecnologia dei Web service, abbiamo notato un pattern. Ogni volta che applichiamo le tecnologie degli Web service ad un problema di integrazione di applicazioni, emerge un pattern. Chiamiamo questo pattern, Architettura Orientata al Servizio, service-oriented architecture (SOA). SOA è un semplice concetto, che lo rende applicabile ad un ampia varietà di situazioni di Web service. Fig. 4.3 – Service-oriented architecture (SOA). - 74 - CAP. 4 - TECNOLOGIE SOFTWARE In ogni service-oriented architecture si individuano tre ruoli: un richiedente del servizio, un fornitore del servizio, e un registro del servizio: • Un service provider è responsabile della creazione della descrizione del servizio, della pubblicazione della descrizione del servizio su uno o più service registry, and receiving Web service invocation messages from one or more service requestors. Un service provider, quindi, può essere una qualsiasi società che mantiene un Web service rendendolo accessibili su un qualche rete. Si può pensare ad un service provider come al "lato server" di una relazione client-server tra un service requestor e il service provider. • Un service requestor è responsabile della ricerca di una descrizione di un servizio pubblicato su uno o più service registry ed è responsabile dell’usare la descrizione di un servizio per collegare o invocare i Web service mantenuti dagli service provider. Qualsiasi utente di un Web service può essere considerato un service requestor. Si può pensare ad un service requestor come al "client side" di una relazione client-server tra un service requestor e il service provider. • Il service descrizione registry dei Web è responsabile service di pubblicate mostrare dai le service provider e di permettere ai service requestor di ricercare la collezione di descrizioni dei servizi contenuti all’interno - 75 - CAP. 4 - TECNOLOGIE SOFTWARE del service registry. Il ruolo del service registry è semplice: essere l’intermediario tra service requestor e service provider. Una volta che il service registry crea il collegamento, non è dell’interazione avviene più necessario; direttamente tra il il resto service requestor e il service provider con l’invocazione del Web service. Ciascuno di questi ruoli può essere svolto da un qualsiasi programma o nodo della rete. In alcune circostanze, un singolo programma potrebbe svolgere più ruoli; per esempio, un programma può essere un service provider, che fornisce un Web service a degli utenti come pure un service requestor, che usa Web service forniti da altri. Una SOA include inoltre tre operazioni: publish, find, and bind. Queste operazioni definiscono dei contratti tra i ruoli nella SOA: • L’operazione di publish è un atto di registrazione del servizio o di avviso di servizio. Costituisce il contratto tra il service registry e l service provider. Quando un service provider pubblica le descrizioni dei suoi Web service sul service registry, rende noti i dettagli di quel Web service ad una comunità di service requestor. I dettagli attuali della API di publish dipendono da come è stato implementato il service registry. In alcuni semplici scenari o nel "direct publish", il ruolo service registry è - 76 - CAP. 4 - TECNOLOGIE SOFTWARE svolto dalla rete stessa, l’operazione publish è semplicemente l’azione di spostare la descrizione del servizio nella struttura a cartelle di un Web application server. Altre implementazioni di service registry, come l’UDDI, definisce una implementazione molto sofisticata dell’operazione di publish. • L’operazione di find è il logico duale dell’operazione di pubblicazione. L’operazione di ricerca è il contratto tra un service requestor e un service registry. Con l’operazione di ricerca, il service requestor dichiara un criterio di ricerca, come il tipo di servizio, molti altri aspetti del servizio come le garanzie di qualità del servizio, e così via. Il service registry confronta il criterio di ricerca con la collezione di descrizioni di Web service pubblicati. Il risultato dell’operazione di ricerca è una lista di descrizioni di servizi che soddisfano il criterio di ricerca. Ovviamente, la sofisticatezza dell’operazione di ricerca varia con l’implementazione del ruolo di service registry. I Semplici service registry possono fornire un’operazione di ricerca niente di più complesso di una HTTP GET senza parametri. In questo caso l’operazione di ricerca ritorna sempre tutti gli Web service pubblicati sul service registry ed è compito del service requestor di stabilire quale descrizione di Web service corrisponde alle proprie esigenze. L’UDDI, ovviamente, fornisce delle capacità di ricerca estremamente potenti. - 77 - CAP. 4 - TECNOLOGIE SOFTWARE • L’operazione di bind racchiude la relazione client-server tra il service requestor e il service provider. L’operazione di bind può essere abbastanza sofisticata e dinamica, come la generazione on-the-fly del proxy lato-client basato sulla descrizione del servizio, usato per invocare il Web service; o può essere un modello molto statico, dove uno sviluppatore scrive a mano come l’applicazione client richiama un Web service. La chiave del SOA è la descrizione del servizio. È la descrizione del servizio che è pubblicata dal service provider sul service registry. È la descrizione del servizio che viene recuperata dal service requestor come risultato di un’operazione di ricerca. È la descrizione del servizio che dice al service requestor tutto quello che deve conoscere in modo da collegare o invocare il Web service fornito dal service provider. La descrizione del servizio indica quale informazione (se presente) è ritornata al service requestor come risultato di un’invocazione di uno Web service. - 78 - CAP. 4 - TECNOLOGIE SOFTWARE 4.2.1 Web Services Fig. 4.4 – Flusso procedurale di una chiamata ad un servizio web. Un Web Services è un insieme di standard di comunicazione che permettono a diverse applicazioni di scambiarsi dati e servizi applicative. Lo scenario ipotizzato è quello di un’applicazione che necessita, per espletare le sue funzioni, di una serie di - 79 - CAP. 4 - TECNOLOGIE SOFTWARE servizi . Si vogliono reperire altrove questi servizi invece di svilupparli all’interno dell’applicazione stessa. I Web Services non sono stati la prima formalizzazione di architettura distribuita;ma rispetto alle precedenti questa architettura offre dei vantaggi che l’hanno fatta preferire alle altre: semplicità della specifica; utilizzo di tecnologie standard; ampio consenso da parte di diverse aziende (Microsof, Sun, IBM….) la presenza di tool che aiutano enormemente la creazione di nuovi servizi e la fruizione dei servizi esistenti; Tutto ciò ha portato all’adozione dei Web Services in diversi contesti. Come accade spesso, avere numerose implementazioni, sia di servizi che di strumenti per realizzarli, porta la specifica a diventare uno “standard di fatto” . Semplicità della specifica. Più una specifica è semplice , minore è il tempo che trascorre tra il suo studio e la realizzazione di progetti concreti. Le tecnologie che stanno alla base dei Web Services sono molto semplici, intuitive e con poche regole di base. Utilizzo di tecnologie standard. Tutte le tecnologie alla base dei Web Services sono standard(essenzialmente XML per la rappresentazione, XML schema per la validazione e Http per il trasporto anche se esso si può - 80 - CAP. 4 - TECNOLOGIE SOFTWARE basare su qualsiasi altro protocollo . L’XML come scelta di base permette, inoltre , anche ad una persona di “guardare” i messaggi ed di intuirne il significato. Ampio consenso da parte di diverse aziende. Microsoft è stata la prima comunicazione azienda basate su a proporre XML. Però modalità ha avuto di la lungimiranza di non mantenere proprietarie le specifiche ma di renderle pubbliche. Questo ha portato ad un crescente interesse all’affermarsi dei primi standard. Presenza di tool. La loro presenza è una conseguenza dei punti precedenti ma è l’unico vero motivo che riesce a catalizzare una quantità sempre crescente di sviluppatori. I Web Services sono un ottimo strumento dove l’interoperabilità è un requisito essenziale. Per altri contesti e soprattutto in quelli in cui si ha un completo controllo sulle tecnologie adottabili nelle applicazioni coinvolte, i Web Services non sono una scelta ottimale:la loro generalità implica un volume di traffico notevole e non ottimizzato e un carico computazionale significativo sia per la codifica che la decodifica dei dati. - 81 - CAP. 4 - TECNOLOGIE SOFTWARE Fig. 4.5 – Stack di collegamento tra messaggio SOAP e protocollo di rete HTTP. Lo stack di collegamento rappresenta le tecnologie che determinano come un messaggio viene spedito dal richiedente del servizio al fornitore del servizio. La base dello stack è un protocollo di rete (Network Protocol). I Web Service possono essere basati su una varietà di standard, i protocolli di collegamento di Internet come HTTP o HTTPS, SMTP, FTP, e via dicendo, come pure sofisticati protocolli di livello enterprise come RMI/IIOP e MQSeries. Per la codifica dei dati (Data Encoding), i Web service usano l’XML. In aggiunta, un contenuto non XML può essere referenziato da Web service con messaggio invocato, permettendo la massima flessibilità nei tipi di dati usati nel messaggio. Per le specifiche dei dati, i Web service usano l’XML Schema. Questo include sia gli schemi nel contesto - 82 - CAP. 4 - TECNOLOGIE SOFTWARE del messaging XML sia gli schemi conformi ad un set di regole predefinite, come le regole della codifica SOAP. Costruito sopra gli strati di networking protocol e dataencoding sono gli strati di messaging XML. Per l’XML Messaging, i Web service usano il SOAP nel data encoding, nello stile di interazione, e nelle variazioni di binding dei protocolli. SOAP è usato come un semplice approccio per inserire un messaggio XML in una busta. Il risultato è una base solida di standard per i Web service. Concettualmente al di sopra il meccanismo di SOAP enveloping è un meccanismo per estensioni dell’envelope chiamati SOAP header. Con i SOAP header, le estensioni ortogonali come una firma digitale possono essere associati con il corpo del messaggio contenuto all’interno del SOAP envelope. Gli strati di questo stack sono ben definiti, sia come protocolli di rete standard che come specifiche SOAP stesse. Lo stack è il più diffuso e più ampiamente supportato set di tecnologie per i Web service. A destra nella figura ci sono tre colonne verticali che rappresentano le tecnologie associate che coinvolgono più livelli dello stack di collegamento. La sicurezza, per esempio, può intervenire ad ogni livello – l’SSL a livello di - 83 - CAP. 4 - TECNOLOGIE SOFTWARE protocollo di rete e le firme digitale a livello delle estensioni dell’envelope. È fuori di ogni dubbio che non avremmo mai un singolo standard che copra tutti gli aspetti della sicurezza di cui hanno bisogno i Web service. Le altre colonne verticali elencate includono la qualità del servizio e la manutenzione. Questi sono solo alcuni degli aspetti degli Web service che possono intervenire in più levelli dello stack di collegamento, ma non ci sono standard universalmente accettati. 4.2.2 Protocollo SOAP SOAP nasce come uno standard aziendale, progettato per migliorare l'interoperabilità della multipiattaforma, frutto del lavoro congiunto di colossi quali Microsoft, DevelopMentor, IBM, Lotus Development, UserLand e Sun Microsystems. La comunicazione da applicazione ad applicazione su Internet e/o su Intranet ha impegnato tantissimo il popolo degli sviluppatori. La maggior parte delle soluzioni individuate tendono ad essere specifiche della piattaforma, non scalano molto bene, spesso richiedono molti round-trip tra il Client e il Server. Altre soluzioni a questo tipo di problemi sono DCOM, CORBA, ecc. Le stesse comunque, incontrano non pochi problemi nel passare attraverso i - 84 - CAP. 4 - TECNOLOGIE SOFTWARE FireWall. Il protocollo SOAP riduce molti di questi problemi, la specifica completa di SOAP è disponibile sul Web site di Microsoft. Il protocollo SOAP fornisce un modo consistente e strutturato di trasporto dei dati e di chiamata dei metodi fra le potenziali applicazioni distribuite. L'esecuzione di questi servizi basati sul WEB necessita di numerosi componenti posti sui vari computer. Poiché questi sistemi comprendono molti computer, inclusi client, server di medio livello e DBMS, sono denominati sistemi distribuiti. Sistemi distribuiti Solitamente i sistemi distribuiti utilizzano due modelli di comunicazione: • Trasferimento di messaggi • Richiesta/risposta Il primo modello consente la comunicazione tra gli attori mediante lo scambio di messaggi che possono essere spediti in ogni momento, questo provoca un'azione da parte del destinatario del messaggio, che a sua volta viene "risvegliato" per eseguire l'azione associata. L'elaborazione dei messaggi può essere: • Sincrona • Asincrona Sincrona messaggio, quando, il a fronte destinatario della spedizione immediatamente del inizia l'esecuzione dell'azione associata. Asincrona quando, tra trasmettitore/ricevitore è interposta una coda di messaggi, - 85 - CAP. 4 - TECNOLOGIE SOFTWARE e solo quando il ricevitore richiede di riceverli in coda li elabora. Ciò può essere fatto anche dopo molto tempo rispetto all'invio del messaggio. Nel modello richiesta/risposta, la richiesta e la risposta sono unite e quindi si parla di sistema sincrono. La richiesta viene inoltrata da un'applicazione e questa, prima di continuare con l'elaborazione, richiesta/risposta è attende i risultati. utilizzato per Il modello consentire la comunicazione tra i componenti sui diversi computer attraverso le RPC (Remote Procedure Call). Attualmente i due standard più diffusi per l'attivazione remota di procedure sono DCOM (Distributed Component Object Model) e IIOP (Internet Inter-Orb Protocol). Sono entrambi efficaci, anche se non progettati appositamente per l'interoperabilità, quindi non è possibile richiamare da un client un componente su un server prima di conoscere lo standard utilizzato dal server e prima di avere impostato la security. Su intranet è possibile limitare il sistema all'utilizzo di una piattaforma di riferimento, non appena si opera su internet, di solito non è possibile utilizzare una piattaforma uniforme nell'intero sistema. A questo punto DCOM e IIOP non consentono più la comunicazione tra due componenti all'interno del sistema né consentono agli utenti di transitare all'interno di domini affidabili. Per politiche di security, viene bloccato dai Firewall il passaggio di stream binari da porte TCP/IP. SOAP rappresenta una soluzione a questi problemi, sfruttando la - 86 - tecnologia WEB e la CAP. 4 - TECNOLOGIE SOFTWARE flessibilità e l'estensibilità di XML, permette inoltre di transitare all'esterno delle reti intranet, non procurando problemi ai Firewall. Lo standard SOAP non introduce nuovi concetti in quanto si basa su tecnologia già esistente. Attualmente utilizza il protocollo HTTP come protocollo di trasporto di messaggi richiesta/risposta ed è indipendente dalla piattaforma ponendosi inoltre ad uno strato indipendente dal protocollo di trasporto. Teoricamente è possibile effettuare la richiesta utilizzando qualsiasi protocollo di trasporto ed avere la risposta con qualsiasi protocollo di trasporto. Un Package SOAP contiene informazioni che consentono di richiamare un metodo anche se nella specifica SOAP il nome del metodo stesso non viene definito. SOAP, inoltre, non gestisce il garbage-collector distribuito, il boxcarring dei messaggi, la protezione del tipo e HTTP bidirezionale. SOAP consente il passaggio dei parametri e dei comandi tra i client e i server HTTP indipendentemente dalle piattaforme e dalle applicazioni sul client e sul server. I parametri e i comandi sono codificati mediante XML. URI, URL Prima di iniziare a parlare di protocollo HTTP, SOAP ecc. è importantissimo introdurre il significato di alcuni termini, quali URL, URI, URN. Un URI (Uniform Resource Identifier) è semplicemente una stringa formattata che identifica univocamente una risorsa. Un URIs può essere espresso in - 87 - CAP. 4 - TECNOLOGIE SOFTWARE due diversi modi, uno è URLs (Uniform Resource Locators) e URNs (Uniform Resource Names). Una URL codifica il protocollo necessario per localizzare la risorsa specificata. Una URN è indipendente dalla posizione e non implica alcun protocollo o meccanismo per localizzare la risorsa specificata. Una URL inizia con il prefisso che identifica il protocollo, lo scheletro di una URL è ( [] indica opzionale ): <protocol>"://" <host> [":"<port>] [<path> ["?" <queryString>]] <host> indica l'indirizzo IP del server, <port> è il numero di porta TCP su cui è in attesa il server, se non è specificato nessuna porta per default si assume la porta 80, e il <path> è l'URI nell'intestazione assoluto della passato richiesta con HTTP, Request-URI se non viene specificato nessun path per default si assume "\" (root). Una URN a differenza della URL è poco comprensibile, una URN è una stringa univoca (molto simile ad UUID). Non esiste un modo generico per dereferenziare una URN per cercare la risorsa che identifica, la sintassi utilizzata per definire una URN è: "urn:" <NID> ":" <NSS> <NID> è l'indentificatore del namespace, e <NSS> è la stringa che specifica il namespace, - 88 - ad esempio una CAP. 4 - TECNOLOGIE SOFTWARE interfaccia di un componente COM può essere rappresentata dalla seguente URN: urn:uuid:00020906-0000-0000-C000-000000000046 Le URN è il meccanismo preferito per riferirsi a risorse mantenendosi indipendenti dalla locazione della risorsa stessa, questo meccanismo è pesantemente utilizzato in XML, SOAP. HTTP come protocollo di trasporto È pratica comune usare DCOM o CORBA all'interno di un server farm ed usare http per entrare nel farm da una macchina client. HTTP è un protocollo RPC-like molto semplice da installare, ed è molto più semplice farlo "passare" attraverso i Firewall rispetto ad altri protocolli. Una richiesta http è tipicamente inviata ad un WebServer (ad esempio IIS, Apache, Tomcat) anche se un numero sempre più crescente di applicazioni supporta direttamente HTTP come protocollo nativo in aggiunta a DCOM e IIOP. Come DCOM e IIOP, lo strato HTTP comunica con il protocollo TCP/IP, la porta standard utilizzata è la 80, ma qualunque altra porta può essere utilizzata allo stesso scopo. Dopo aver stabilito una connessione TCP, il client spedisce un messaggio di richiesta HTTP al server,che gli risponde con un messaggio di risposta HTTP dopo aver elaborato la richiesta stessa. Sia richiesta che risposta - 89 - CAP. 4 - TECNOLOGIE SOFTWARE contengono un'arbitraria informazione PAYLOAD, tipicamente rappresetanta dalle intestazioni Content-Length e Content-Type. Di seguito è riportato un esempio di richiesta HTTP valida: POST /target http/1.1 Host: 128.0.0.1 Content-Type: text/plain Content-Length: 25 Esempio di richiesta http Come si può notare, l'intestazione HTTP è rappresentata da testo, è molto semplice trovare eventuali problemi HTTP utilizzando uno sniffer o un tool testuale, ad esempio TELNET. La natura testuale di HTTP lo rende adattabile a qualsiasi ambiente di sviluppo Web-Based. La prima linea della richiesta HTTP contiene tre componenti: il metodo HTTP , la richiesta URI e la versione del protocollo. La IETF (Internet Engineering Task Force) ha standardizzato il numero dei possibili metodi HTTP, questi sono GET e POST. Il Metodo GET è utilizzato per la navigazione nel WEB, il metodo POST è comunemente utilizzato per lo sviluppo di applicazioni. I metodi GET/POST possono essere utilizzati indifferentemente per inviare informazioni dal client al server. La richiesta URI (Uniform Resource Identifier) è un token usato dai server HTTP per identificare il destinatario della richiesta (RFC2616). Il server HTTP dopo aver - 90 - CAP. 4 - TECNOLOGIE SOFTWARE elaborato la richiesta, rimanda indietro al client una risposta. La risposta contiene lo stato che indica la buona o cattiva elaborazione della richiesta stessa. Un esempio di risposta HTTP è: 200 OK Content-Type: text/plain Content-Length: 16 Risposta OK HTTP In questo caso, il server ritorna il codice 200. Con questo codice, si indica in modo standard HTTP la corretta elaborazione della richiesta. Nel caso in cui il server non è stato in grado di elaborare la richiesta, la risposta del server può essere ad esempio: 400 Bad Request Content-Length: 0 Il server destinatario HTTP decide definita che nella la richiesta URI deve inviata al essere temporaneamente rediretta su una differente URI, la risposta ritornata dal server è: 307 Temporarily Moved Location: <http://128.0.0.1/target2> - 91 - CAP. 4 - TECNOLOGIE SOFTWARE Content-Length: 0 Questa risposta avverte il client che per essere soddisfatta la richiesta, questa deve essere reinoltrata alla nuova URI definita in Location. Per eventuali approfondimenti sui codici dello stato standard, si rimanda alla RFC2616. SOAP Fig. 4.6 – Struttura di un messaggio SOAP. SOAP viene codificato mediante l'utilizzo di XML tramite il modello richiesta/risposta usando come protocollo di trasporto HTTP. Un metodo SOAP è semplicemente una richiesta HTTP e una risposta alle specifiche di decodifica SOAP. Come CORBA /IIOP, SOAP non richiede che un oggetto specifico sia legato ad un dato end-point ma spetta allo sviluppatore decidere come mappare l'oggetto endpoint su un oggetto presente sul server. L'end-point SOAP è una URL http che identifica - 92 - il metodo destinatario CAP. 4 - TECNOLOGIE SOFTWARE dell'invocazione. Una richiesta SOAP è una richiesta HTTPPOST. Per indicare il metodo da invocare si usa l'intestazione HTTP SOAPMethodName, un esempio: SOAPMethodName: urn:myurn:Iservice#add In questa intestazione il nome del metodo è indicato dopo il segno # ed è add. Il payload HTTP di una richiesta SOAP è un documento di XML che contiene i valori [in] e [in,out] dei parametri del metodo. Questi valori sono codificati come elementi figli di un elemento distinguibile di chiamata, condiviso con il nome, l'URI del namespace e dell'interstazione HTTP di SOAPMethodName. L'elemento della chiamata deve comparire all'interno del messaggio standard SOAP composto da <Envelope> e <Body>, un esempio di richiesta minima di un metodo SOAP è riportata di seguito: POST /string_server/Object17 HTTP/1.1 Host: 128.0.0.1 Content-Type: text/xml Content-Length: 152 SOAPMethodName: urn:strings-com:object#method <Envelope> <Body> <m:method xmlns:m='urn:strings-com:object'> - 93 - CAP. 4 - TECNOLOGIE SOFTWARE <param>Hello</param > </m:method > </Body> </Envelope> Nell'intestazione elemento figlio SOAPMethodName dell'elemento è <Body>, inserito un altrimenti la chiamata viene rifiutata. Ciò permette agli amministratori del firewall di realizzare filtri di chiamata a particolari metodi senza passare dal parser XML. Il formato di risposta di SOAP è simile a quello della richiesta. Il payload di risposta conterrà i parametri [out] e [in,out] del metodo messi come elementi figli di un elemento distinguibile della risposta. Il nome di questo elemento è lo stesso dell'elemento di chiamata della richiesta concatenato con il suffisso Response. Di seguito è riportato un esempio minimo di risposta: 200 OK Content-Type: text/xml Content-Length: 132 <Envelope> <Body> <m:methodResponse xmlns:m= 'urn:strings-com:object '> <result>4</result> </m:methodResponse> - 94 - CAP. 4 - TECNOLOGIE SOFTWARE </Body> </Envelope> In questo caso l'elemento di risposta è chiamato methodResponse, che è il nome di metodo seguito dal suffisso di Response. Inoltre c'è da notare che l'intestazione del HTTP SOAPMethodName è assente. L'intestazione è richiesta soltanto nel messaggio di richiesta, non nella risposta. DataType Ogni elemento di un messaggio SOAP è un elemento strutturale di SOAP, un elemento della root, un accessor, o un elemento indipendente. I soap:Envelope, soap:Body e soap:Header sono gli unici tre elementi strutturali di SOAP. La relazione base è descritta dal seguente frammento dello schema di XML: <schema targetNamespace='urn:schemas-xmlsoaporg:soap.v1'> <element name='Envelope'> <type> <element name='Header' type='Header' minOccurs='0' /> <element name='Body' type='Body' minOccurs='1' /> - 95 - CAP. 4 - TECNOLOGIE SOFTWARE </type> </element> </schema> Dei quattro tipi di elementi di SOAP, gli elementi strutturali sono utilizzati per rappresentare le istanze dei tipi, o riferimenti a istanze di tipi. Un elemento root è un elemento distinto, <soap:Body> discende oppure direttamente dall'elemento dall'elemento <soap:Header>. L'elemento <soap: Body> ha esattamente un elemento root, questo può rappresentare sia la chiamata, sia la risposta che eventuali elementi di fault. Questo elemento root deve essere il primo elemento child di <soap:Body> e deve essere corredato del URI del namespace che deve corrispondere all'intestazione HTTP SOAPMethodName, o soap:Fault nel caso di un messaggio di fallimento dell'invocazione del servizio. L'elemento di <soap:Header> può avere più elementi root, di cui uno per l'estensione dell'intestazione elementi root connessa devono con essere il messaggio. discendenti Questi diretti di <soap:Header> ed i loro URI del namespace indicano l'estensione del tipo di dati utilizzati. Gli elementi accessor sono usati per rappresentare i campi, le proprietà, o i membri del tipo di dati. Ogni campo tipo di dato avrà esattamente un elemento accessor rappresentazione di SOAP. - 96 - nella relativa CAP. 4 - TECNOLOGIE SOFTWARE Per capire bene come si utilizzato gli elementi accessor, consideriamo la seguente definizione di classe Java: public class myTest { public int ivalue; public long lvalue; } serializzando mediante SOAP questa classe, abbiamo: <t:myTest xmlns:t='urn:tst-com:java:myTest'> <ivalue>3514</ivalue > <lvalue>100</lvalue > </t:myTest> gli elementi accessor ivalue e lvalue sono chiamati semplici accessor perché corrispondono a tipi primitivi, e sono definiti nella 2°parte del W3X XML Schema (per ulteriori informazioni clicca qui). Questa specifica formalizza i nomi e le rappresentazioni dei tipi numerici, tipo data, tipo stringa, inoltre è un meccanismo per la definizione dei tipi primitivi nuovi usando il costrutto <datatype> all'interno di nuova definizione dello schema. La soluzione più semplice per inserire strutture complesse all'interno di una struttura, è quello di incapsulare il valore della struttura all'interno - 97 - CAP. 4 - TECNOLOGIE SOFTWARE dell'elemento accessor, ad esempio consideriamo la seguente definizione di classe Java: public class myTest1 { public myTest element1; public myTest element2; } come possiamo notare, all'interno della classe sono inseriti due oggetti precedentemente specificati, quindi, serializzando la classe avremo: <t:myTest1 xmlns:t='urn:tst-com:java:myTest1'> <element1 > <ivalue>1</ivalue> <lvalue>2</lvalue> </element1 > <element2 > <ivalue>3</ivalue> <lvalue>4</lvalue> </element2 > </t:myTest1 > In questo caso, i valori degli oggetti direttamente sotto i loro elementi accessor. - 98 - sono inseriti CAP. 4 - TECNOLOGIE SOFTWARE Elementi Indipendenti In SOAP, un elemento indipendente, rappresenta una istanza di un tipo che si riferisce ad almeno un elemento accessor multireference. Tutti gli elementi indipendenti sono etichettati dall'attributo di <soap:id> ed il valore di questo attributo deve essere unico in tutto l'Envelope di SOAP. SOAP definisce un attributo (soap:Package) che può essere applicato ad un elemento. L'attributo è usato per controllare dove gli elementi indipendenti possono essere codificati. Le regole di serializzazione di SOAP impongono che un elemento indipendente deve essere codificato come un discendente diretto di un elemento <soap:Header>, <soap:Body> oppure un elemento che è marcato come <soap:Package='true'>. Annotando un elemento come Package, si garantisce che l'elemento XML che codifica l'istanza è completamente selfcontained e non ha multireference accessori ad elementi che sono esterni al package. Array Gli array in SOAP sono trattati come caso speciale compound type. Un array SOAP deve avere un Rank (dimensione) e una capienza. Un array viene trattato come un compound type, ogni elemento di array ha come subelement il nome e il tipo definito nel namespacequalified. Consideriamo ad esempio la seguente definizione di un tipo COM IDL: - 99 - CAP. 4 - TECNOLOGIE SOFTWARE struct OBJECTLIST { int length; [size_is(length)] OBJECT obj[]; }; una istanza di questo tipo viene serializzata come: <t:OBJECTLIST xmlns:t='uri'> <length>5</length> <obj xsd:type='t:OBJECT[5]'> <OBJECT><elem>1</elem></OBJECT> <OBJECT><elem>2</elem></OBJECT> <OBJECT><elem>3</elem></OBJECT> <OBJECT><elem>4</elem></OBJECT> <OBJECT><elem>5</elem></OBJECT> </obj> </t:OBJECTLIST> se gli oggetti sono marcati con l'attributo [ptr], la codifica sarà fatta mediante l'utilizzo dei multireference accessor, un esempio di questa codifica è di seguito riportata: <t:OBJECTLIST xmlns:t='uri'> <length>5</length> <obj soap:href="#x5"/> </t:OBJECTLIST> - 100 - CAP. 4 - TECNOLOGIE SOFTWARE <t:ArrayOfOBJECT soap:id='x5' xsd:type='t:OBJECT[5]'> <OBJECT><elem>1</elem></OBJECT> <OBJECT><elem>2</elem></OBJECT> <OBJECT><elem>3</elem></OBJECT> <OBJECT><elem>4</elem></OBJECT> <OBJECT><elem>5</elem></OBJECT> </t:ArrayOfOBJECT> Quando si codifica un array come un elemento indipendente, il nome del tag è costruito inserendo al nome del tipo il prefisso ArrayOf. Elemento FAULT Un elemento FAULT può contenere quattro elementi figli: faultcode, faultstring, faultactor e detail. È possibile trovare l'intera lista dei codici di fault al seguente indirizzo . I valori di codice attualmente disponibili sono: • VersionMismatch: questo errore viene restituito quando la chiamata ha utilizzato uno spazio non valido • MustUnderstand: quando il destinatario non ha riconosciuto un elemento XML ricevuto, che contiene un elemento con un tag mustUnderstand="true". • Client: classi di errore causate da informazioni improprie nel messaggio SOAP effettivo, questi errori rappresentano un problema che riguarda il contenuto del - 101 - CAP. 4 - TECNOLOGIE SOFTWARE messaggio effettivo e indicano che il messaggio non deve essere inviato nuovamente senza modifica. • Server: tali errori riguardano i problemi con il server e di solito non rappresentano problemi con il messaggio SOAP. • L'elemento faultstring è una stringa non utilizzata dalle applicazioni ma utilizzata solo come Struttura richiesta SOAP. • messaggio per l'utente, l'elemento faultfactor può fornire informazioni sugli elementi che hanno provocato l'errore nel percorso del messaggio SOAP, l'elemento faultactor invece è una URI che identifica l'origine: se l'errore si verifica in un'applicazione che non è la destinazione finale del messaggio, l'elemento faultactor deve essere incluso, altrimenti non è necessario. L'elemento detail è necessario se i contenuti dell'elemento Body (SOAP) non possono essere elaborati, questo viene utilizzato per fornire informazioni sugli errori specifici dell'applicazione. Un esempio di risposta SOAP contenente un fault è proposto qui di seguito: HTTP/1.1 200 OK Content-Type: text/xml Content-Length: <SOAP-ENV:Envelope - 102 - CAP. 4 - TECNOLOGIE SOFTWARE xmlns:xsi= <http://www.w3.org/1999/XMLSchema/instance> xmlns:SOAP-ENV=<http://schemas.xmlsoap.org/ soap/envelope>> <SOAP-Env:Body> <SOAP-ENV:Fault> lt;SOAP-ENV:faultcode>200</SOAP-ENV:faultcode> <SOAP-ENV:faultstring >Must Understand Error </SOAP-ENV: faultstring > <SOAP-ENV:detail xsi:type="Fault" > <errorMessage> Object non installed on server </errorMessage> </SOAP-ENV: detail > </SOAP-ENV:Fault> </SOAP-Env:Body> </SOAP-ENV:Envelope> - 103 - CAP. 4 - TECNOLOGIE SOFTWARE 4.3 APPLICATION SERVER 4.3.1 Apache Tomcat Struttura di una applicazione web La nozione di applicazione web è stata formalizzata per la prima volta nella versione 2.2 delle specifiche delle servlet Java. Sebbene il concetto di applicazione web sia piuttosto ampio e dipendente dal contesto in cui viene esaminato, la definizione fornita nelle suddette specifiche risulta di portata sufficientemente generale. Dal punto di vista dello sviluppatore di applicazioni web basate su Java risulta particolarmente importante la standardizzazione della struttura di tali applicazioni e l'introduzione degli archivi WAR come strumento per il deployment delle applicazioni web. Una applicazione web è una gerarchia di file e directory disposti secondo uno schema standard. /webapp. Una applicazione web ha una propria directory radice (root). Il nome della cartella corrisponde a ciò che nella terminologia delle servlet viene chiamato context path (nel seguito supporremo che tale nome di tale cartella sia - 104 - CAP. 4 - TECNOLOGIE SOFTWARE webapp). La directory radice contiene tutti gli altri elementi che compongono l'applicazione web. File HTML, JSP, ... La directory /webapp contiene i documenti HTML, le pagine JSP, le immagini e le altre risorse che compongono l'applicazione web. Tali file possono essere strutturati in directory esattamente come si farebbe per un sito web statico. /webapp/WEB-INF. All'interno della cartella root si trova una directory speciale, denominata WEB-INF. La funzione di questa cartella è quella di contenere file riservati, come ad esempio il file di configurazione dell'applicazione, web.xml. Per questo motivo le specifiche impongono che la cartella ed il suo contenuto debbano risultare inaccessibili per gli utenti dell'applicazione. /webapp/WEB-INF/web.xml. Il file web.xml viene comunemente denominato deployment descriptor. Si tratta del file di configurazione dell'applicazione web; in esso, ad esempio, si definiscono gli alias delle servlet, i parametri di inizializzazione, le mappature dei percorsi e così via. /webapp/WEB-INF/classes e /webapp/WEB-INF/lib. Queste due directory sono destinate a contenere le classi e gli archivi Jar di cui la nostra applicazione web necessita. Se dobbiamo utilizzare delle classi Java compilate (file con estensione .class andremo a - 105 - copiarle all'interno di CAP. 4 - TECNOLOGIE SOFTWARE /webapp/WEB-INF/classes, secondo l'usuale struttura di directory Java (così se abbiamo una classe MiaServlet contenuta nel package sito.servlet. il percorso del file compilato sarà /webapp/WEB- INF/classes/sito/servlet/MiaServlet.class). Se dobbiamo utilizzare classi contenute in archivi Jar, invece, sarà sufficiente copiarli in /webapp/WEB-INF/lib. Archivi WAR Una volta sviluppata una applicazione web possiamo "impacchettarla" in un archivio WAR (Web Application Archive). Un archivio WAR non è altro che un archivio Jar, una vecchia conoscenza dei programmatori Java, la cui estensione viene modificata in .war semplicemente per indicare che il contenuto dell'archivio è una applicazione web. Gli archivi WAR hanno una duplice funzione: oltre a quella ovvia di comprimere tutte le componenti di una applicazione in un unico archivio, costituiscono il formato di deployment dell'applicazione stessa. Il termine inglese deployment indica la messa in opera dell'applicazione. Uno scenario tipico è quello in cui l'applicazione viene sviluppata localmente per poi essere trasferita, ad ultimazione, sul server vero e proprio (detto anche di produzione). In questo caso è sufficiente creare il file WAR, copiarlo sul server di produzione e seguire le istruzioni per l'installazione del WAR relative al servlet container che si - 106 - CAP. 4 - TECNOLOGIE SOFTWARE sta utilizzando. Nel caso di Tomcat, l'installazione di un WAR è estremamente semplice: è sufficiente copiare il file nella directory webapps e avviare (o riavviare) il server, che provvederà da solo a decomprimere l'archivio e ad avviare l'applicazione. Un esempio di comando per la creazione di un archivio WAR (da digitare dopo essersi spostati all'interno della cartella webapp) è il seguente: jar cvf webapp.war . È consigliabile, in ogni caso, fare riferimento alla documentazione ufficiale del tool jar per una spiegazione esaustiva delle opzioni disponibili. In questa breve nota abbiamo preso dimestichezza con la strutturazione di una applicazione web, così come definita nelle specifiche Servlet a partire dalla versione 2.2. Organizzando le nostre applicazioni in tal modo otterremo diversi vantaggi: • aumenteremo la portabilità dell'applicazione, rendendone più semplice la messa in opera; • renderemo l'applicazione più comprensibile ad altre persone, in quanto sarà molto più semplice sapere dove si trovano le classi, gli archivi Jar e le altre componenti dell'applicazione stessa; - 107 - CAP. 4 - TECNOLOGIE SOFTWARE • potremo racchiudere l'intera applicazione in un archivio WAR, rendendone semplice l'installazione su tutti i servlet container conformi alle specifiche Sun. Abbiamo così compiuto il primo passo per la realizzazione di una applicazione web in ambiente Java, che abbiamo imparato a strutturare in modo standard. Il prossimo argomento da affrontare sarà la configurazione dell'applicazione attraverso il già citato file web.xml. L'ambiente di Tomcat Tomcat contiene al suo interno tutte le funzionalità tipiche di un web server, ovvero - in sintesi - ha la capacità di interpretare una richiesta di una risorsa veicolata su protocollo HTTP, indirizzarla ad un opportuno gestore (o prenderla dal filesystem) e restituire poi il risultato (codice HTML o contenuto multimediale che sia). In questo aspetto non differisce molto da altri web server, come Apache, se non che spesso si rivela meno prestante. La caratteristica innovativa di Tomcat non è quindi quella di essere un veloce web server, ma piuttosto quella di fornire allo sviluppatore un vero e proprio ambiente nel quale girano applicazioni Java (le servlet). Adesso cercheremo di comprendere meglio come sia strutturato questo ambiente Java nel quale prenderanno vita le nostre applicazioni latoserver. - 108 - CAP. 4 - TECNOLOGIE SOFTWARE Contenitori in Tomcat Come nelle normali applicazioni, anche nella programmazione lato server gli oggetti creati devono avere uno scopo, ovvero essere definiti ed accessibili in un determinato contesto. suddividere lo Utilizzare spazio delle Tomcat risorse equivale in a contenitori sostanzialmente indipendenti nei quali possono essere depositati oggetti Java, secondo una logica orientata al web. Per comprendere appieno questo concetto analizziamo nel dettaglio i singoli contenitori che sono tra loro strutturati in maniera abbastanza gerarchica. Tomcat Chiaramente il primo livello corrisponde al web server stesso che avrà un insieme di variabili ad esso associate. Ad esempio qui dentro vi sarà indicazione del numero di thread attualmente in esecuzione, della versione di Tomcat o altre proprietà globali generalmente di poco interesse nella programmazione. Contesto Quando un utente accede al web server accede in realtà ad una sezione specifica del web server (a un certo dominio virtuale o directory, ad esempio) identificata sostanzialmente dalla URL. Nel gergo di Tomcat la sezione viene chiamata contesto (context). All'interno di questo - 109 - CAP. 4 - TECNOLOGIE SOFTWARE contenitore si troveranno quindi oggetti comuni a tutta la sezione, che saranno gli stessi per tutti gli utenti e tutte le applicazioni generato del contesto. all'avvio indipendentemente di Questo contenitore Tomcat dal fatto e che viene rimane qualcuno attivo lo stia interrogando in quel momento. Questo non vuol dire che rimanga invariato, anzi, il suo contenuto cambierà in funzione delle richieste ricevute ed ogni elemento contribuirà a definire l'ambiente di tutta la sezione. Sessione Il primo contenitore con caratteristiche fortemente orientate al web è quello associato alla sessione (Session). Come dice il nome questo contenitore viene associato ad un utente (o, meglio, ad un client) per tutto il tempo per il quale rimane connesso ad un contesto specifico. Al suo interno verranno memorizzati oggetti che saranno accessibili solo a quell'utente e ne personalizzeranno l'ambiente rispetto ad altri client che utilizzino risorse dello stesso contesto nello stesso momento. Richiesta Il contenitore con la vita più breve è la richiesta (Request) che nasce e muore nell'ambito della singola transazione web. Raramente questo contenitore viene riempito da applicazioni anche se svolge un ruolo fondamentale nel flusso dei dati che attraversano Tomcat. - 110 - CAP. 4 - TECNOLOGIE SOFTWARE Il flusso di dati Abbiamo visto che l'oggetto Context viene creato all'avvio di Tomcat mentre l'oggetto Request nasce muore ad ogni richiesta. Andiamo ad analizzare un po' più in dettaglio il flusso logico che segue Tomcat nel processare le singole richieste web che riceve. Richiesta di una risorsa Supponiamo che un client acceda a una risorsa su di un server su cui gira Tomcat. Innanzi tutto viene instanziato un oggetto Request, Req, in Tomcat nel quale vengono inserite tutte le informazioni ricevute tramite HTTP (dalla url richiesta sino ai cookies) e "al suo fianco" viene instanziato un oggetto risposta (Response, Res) nel quale costruire pian piano la risposta da restituire al client. Leggendo la url all'interno della richiesta Tomcat è quindi in grado di comprendere a quale dei contesti residenti in memoria debbano essere consegnati questi due oggetti Req e Res. Contesto e sessione Il contesto invocato da Tomcat provvederà a vedere se esiste già una sessione attiva per quel tipo di client leggendone la "firma" nella richiesta (sotto forma di cookie o nella url, a seconda del metodo usato). Se la sessione, Ses, esiste già tra quelle immagazzinate nel context, il - 111 - CAP. 4 - TECNOLOGIE SOFTWARE contesto la riprende e la affianca agli oggetti Req e Res. A questo punto l'ambiente è stato completamente definito e il web server può passare il controllo all'applicazione associata alla risorsa in questione. Le Servlet A questo punto del percorso logico, il contesto è in grado di decidere (sulla base del suo file di configurazione) a quale applicazione passare il controllo all'interno dell'ambiente di lavoro definito in tutto il percorso (la più semplice delle applicazioni è chiaramente quella che si occupa di restituire un file presente sul disco del server). L'applicazione Java (la servlet) potrà quindi svolgere tutte le sue funzioni attingendo agli oggetti già presenti nei vari contenitori (nella sessione, ad esempio) o mettendocene di nuovi. Risposta Al termine del proprio lavoro la Servlet deve dare un esito al proprio lavoro tramite la risposta Res. Le uniche due modalità per farlo sono o riempirla con dei contenuti che il client sia in grado di comprendere (codice HTML, ad esempio) oppure inserire un ordine per il contesto di redirigere il controllo ad un'altra risorsa (forward). Nel primo caso il contesto ripasserà tutto l'oggetto a Tomcat che si occuperà di generare l'output per il client. Nel secondo caso, se la risorsa è esterna al contesto il - 112 - CAP. 4 - TECNOLOGIE SOFTWARE controllo verrà restituito a Tomcat che ricomincerà il percorso logico con questa nuova risorsa. Se invece la risorsa è interna al contesto un'operazione di forward equivale a fare un passo indietro nel percorso logico per poi tornare avanti su un altro sentiero o, in altri termini, chiamare la Servlet associata alla risorsa indicata nell'istruzione di forward, passandole nuovamente tutto l'ambiente. Installazione di Apache Tomcat Tomcat è un Servlet/JSP engine, o meglio, è l'implementazione ufficiale delle specifiche di queste due tecnologie, che permette di eseguire Servlet e pagine JSP su richiesta di un utente. Esso può essere usato da solo (principalmente utile agli sviluppatori per testing e debugging) o in congiunzione con uno tra i più diffusi web server (per le applicazioni real world), tra i quali: • Apache • Microsoft Internet Information Server • Microsoft Personal Web Server • Netscape Enterprise Server Quindi in base alle esigenze di utilizzo si dovrà installare anche un web server con cui Tomcat coopererà: quando arriva una richiesta di esecuzione di una servlet o JSP, il web server la passa a Tomcat che si occupa di esaudirla. Passiamo adesso all'installazione di Tomcat; nel seguito - 113 - CAP. 4 - TECNOLOGIE SOFTWARE faremo riferimento alla versione stabile disponibile al momento della stesura di questa tesi, e cioè la 5.5.9. Fig. 4.7 – Configurazione del server Tomcat su Eclipse. Tomcat richiede per il suo funzionamento un Java Runtime Enviroment JRE 1.4 o successivo, (compresi, naturalmente i sistemi basati sulla piattaforma Java2). Scaricare l'archivio nel formato più adatto al proprio sistema operativo da qui (Jakarta, il sito ufficiale di Tomcat). Spacchettare l'archivio in una directory (chiamiamola ad esempio foo) dove verrà creata una subdirectory di nome jakarta-tomcat-5.5.9. - 114 - CAP. 4 - TECNOLOGIE SOFTWARE Variabili d'ambiente Cambiare directory spostandosi in jakarta-tomcat-3.2.1 e settare la variabile d'ambiente TOMCAT_HOME in modo da farla puntare alla directory radice dove Tomcat è stato installato. Settare la variabile d'ambiente JAVA_HOME in modo che punti alla directory radice dove è installato il JDK e infine aggiungere il path dell'interprete java alla variabile d'ambiente PATH. Tomcat è pronto adesso per essere eseguito in modalità stand-alone (da solo senza web server). Avvio e arresto di Tomcat Per avviare Tomcat usare lo script o il file batch che si trovano nella directory TOMCAT_HOME/bin. UNIX: % startup.sh Windows: C:\TOMCAT_HOME\bin>startup Per arrestare Tomcat, ci sono due comandi che si trovano nella stessa directory dei comandi di avvio. UNIX: % shutdown.sh Windows: C:\TOMCAT_HOME\bin>shutdown - 115 - CAP. 4 - TECNOLOGIE SOFTWARE Per verificare il corretto funzionamento di Tomcat basta avviare il browser e puntare a http://localhost:9090. Fig. 4.8 – Homepage di Apache Tomcat 5.5.9. La struttura delle directory di Tomcat Vediamo come è struturata la gerarchia delle directory di Tomcat a partire dalla radice. Dopo avere unzippato/untarato Tomcat si pùò vedere che all'interno della directory jakarta-tomcat-5.5.9 abbiamo: - 116 - CAP. 4 - TECNOLOGIE SOFTWARE Tab. 4.1 – Struttura delle directory. NOME DESCRIZIONE CARTELLA bin Contiene gli script/batch per lo startupshutdownd. Contiene i file di configurazione tra cui server.xml che è il principale file di conf configurazione di Tomcat, e web.xml che serve a settare i valori di default per le varie applicazioni gestite da Tomcat. doc Contiene vari documenti riguardanti Tomcat. Contiene diversi file jar usati da Tomcat . lib Su UNIX ogni file in questa directory viene appeso al classpath. logs src webapps Directory utilizzata per i file di log. Sorgenti delle interfacce e delle classi astratte delle API servlet. Contiene esempi di applicazioni web. In aggiunta a queste directory si possono avere anche altre due directory: classes creata dall'utente, dove si possono mettere classi che Tomcat aggiungerà, al suo avvio, al classpath; e work generata automaticamente da Tomcat come directory temporanea (ad esempio per contenere le pagine JSP già compilate, prima di inviarle al - 117 - CAP. 4 - TECNOLOGIE SOFTWARE client). Se si cancella questa directory quando Tomcat è attivo, non si potranno eseguire pagine JSP. Fig. 4.9 – Interfaccia per la gestione delle applicazioni web. 4.3.2 Comunicazione SSL Il protocollo SSL utilizza combinazioni di chiavi pubbliche e simmetriche. Una sessione SSL inizia con uno scambio di messaggi effettuati nella fase detta “Handshake”. - 118 - CAP. 4 - TECNOLOGIE SOFTWARE Fig. 4.10 – Apertura di una sessione SSL. Il server ha un certificato che descrive le informazioni riguardanti la società/ente/persona. I campi più rilevanti sono le informazioni di cui sopra e la chiave pubblica. Il tutto è regolato dal Certificato X.509. Tale certificato può essere garantito da una Certification Authority, la quale assicura l’esatta corrispondenza tra il certificato e colui che lo emette. Anche il client può avere un certificato, ma questo serve solo se esso deve ricevere dati sensibili da parte del server. Generalmente è il contrario. Se la connessione è iniziata dal server, esso invierà al client un messaggio “server hello”. Se è il client ad iniziare la connessione, si avrà in invio un messaggio “hello”. Analizziamo più in dettaglio tale messaggio. È definito dai campi: 1. protocol version: definisce la versione SSl usata; 2. random byte: byte casuali generati dal client; 3. Session identifier: per verificare se si tratta di una nuova sessione o di una aperta in precedenza; 4. Lista delle CipherSuite: è una lista in cui il Client notifica al Server gli algoritmi di crittografia supportati, ordinati in modo decrescente; - 119 - CAP. 4 - TECNOLOGIE SOFTWARE 5. Lista degli algoritmi di compressione, supportati dal Client. Il Server risponde con un messaggio “server hello” definito nel modo seguente: 1. Protocol version: rappresenta la versione SSl scelta dal Server; 2. Random byte: byte generati in modo casuale; 3. Session identifier: identifica la sessione. Se non è una sessione già aperta, se ne crea una nuova; questo nel caso in cui nel tag “session identifier” del messaggio “hello” sino presenti tutti zeri; Altrimenti viene riesumata la sessione indicata dal client, se presente nella cache del server; 4. ChiperSuite: famiglia di algoritmi di crittografia scelta dal server; 5. Compression method: metodo di compressione scelto da server. Dopo questo l’autenticazione scambio tramite lo di messaggi, scambio di un avviene certificato. Supponiamo che debba essere il server ad identificarsi. Questo vuol dire che i dati confidenziali transiteranno dal client al server e NON viceversa, in quanto il Client non è identificato. Tale situazione potrebbe essere, per esempio, una fase di acquisto in rete, in cui il client invia i suoi dati personali al Server, quindi è necessario che il Server sia - 120 - CAP. 4 - TECNOLOGIE SOFTWARE ben identificato. Il Server dunque invia il certificato al Client, insieme alle preferenze riguardo l’algoritmo di crittografia da usare. A questo punto il client verifica che il certificato ricevuto sia valido analizzando vari aspetti. Per prima cosa la data di validità del certificato. In seguito, si verifica che la CA che garantisce il certificato (ammesso che ce ne sia una), sia una CA affidabile. Se lo è, il client recupera la chiave pubblica dalla sua lista di CA sicure per verificare la firma digitale arrivatagli con il certificato del server. Se l’informazione in tale certificato è cambiata da quando è stata firmata da una CA, o se la chiave pubblica nella lista CA non corrisponde alla chiave privata usata dalla CA per firmate il certificato del server, il client non potrà autenticare il server. Infine, il client verifica che il nome del dominio nel certificato server corrisponda allo stesso dominio del server. Questa fase conferma che il server è localizzato nello stesso indirizzo di rete che è specificato nel nome di dominio del suo certificato. Tale controllo previene attacchi del tipo “Man in the Middle”, in quanto se il domain name non corrisponde, molto probabilmente perché il certificato è stato inviato non dal server atteso, ma da una terza persona che si è intromessa. La connessione sarà rifiutata se i due nomi non corrispondono. Se invece tutto è andato a buon fine, il client avrà verificato che il server è affidabile. - 121 - CAP. 4 - TECNOLOGIE SOFTWARE Fig. 4.11 – Attacco di tipo “Man in the middle”. A questo punto il client genera una “pre master key” e la cripta con la chiave pubblica del server. Opzionalmente, in questa fase, può essere richiesta l’autenticazione del client, che è molto simile a quella vista prima, nel seguente modo: esso deve cifrare alcuni valori casuali condivisi con la sua chiave privata, creando in pratica una firma. In più, il server verifica che il client, anche se autenticato, sia autorizzato ad accedere alle risorse richieste. La chiave pubblica nel certificato del client può facilmente convalidare tale firma se il certificato è autentico, in caso contrario la sessione sarà terminata. Il server riceve il messaggio criptato e, verificata l’attendibiltà del client, lo decripta tramite la sua chiave privata. Genera poi una master secret (cosa che fa anche il client, seguendo gli stessi passi) e da questa, insieme, generano la chiave di sessione, una chiave simmetrica che sarà usata per criptare e decriptare i dati che attraversano il tunnel SSL appena creato; inoltre serve per verificare che i dati non vengano alterati nel lasso di tempo che intercorre tra l’invio e la ricezione. A questo punto il client ed il server si inviano un - 122 - CAP. 4 - TECNOLOGIE SOFTWARE messaggio di notifica di HandShake concluso, e lo scambio di dati può iniziare. Installazione e configurazione del supporto SSL su Tomcat 5 1. Creare un keystore eseguendo il seguente comando: Windows: %JAVA_HOME%\bin\keytool -genkey -alias tomcat -keyalg RSA Unix: $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA and specify a password value of "changeit". Scommentare l’elemento "SSL HTTP/1.1 Connector" nel file di configurazione di Tomcat $CATALINA_HOME/conf/server.xml e modificarlo come necessario. <-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 --> <!-- <Connector port="8443" minProcessors="5" maxProcessors="75" - 123 - CAP. 4 - TECNOLOGIE SOFTWARE enableLookups="true" disableUploadTimeout="true" acceptCount="100" debug="0" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" /> --> 2. Usare l’attributo keystoreFile per specificare il percorso del “.keystore”. Attenzione nello specificare un percorso relativo per il file di keystore perchè si parte dalla cartella di installazione del Tomcat mentre il keytool parte dalla cartella base dell’utente inoltre se si usa l’Eclipse è necessario copiare il keystore anche nella cartella dello workspace “.metadata\.plugins\org.eclipse.wst.server.core\tmp 0” altrimenenti quando viene lanciato il Tomcat in Eclipse non trova il file (bisogna apportare le stesse modifiche fatte al file di configurazione $CATALINA_HOME/conf/server.xml anche sul file server.xml del Server project di Eclipse). Si può notare che l’elemento Connector stesso è commentato di default, quindi è necessario rimuovere i tag di commento che lo circondano. Poi, è possibile personalizzare gli attributi specificati come necessario. La discussione seguente analizza solo gli attributi di maggiore - 124 - CAP. 4 - TECNOLOGIE SOFTWARE interesse per impostare la comunicazione SSL, per il resto delle opzioni si può fare riferimento alla guida del Tomcat. L’attributo port (valore di default è 8443) è il numero della porta TCP/IP sulla quale Tomcat si metterà in ascolto per le connessioni sicure. Può essere cambiare con un qualisiasi numero di porta (come con la porta di default per le communicazioni https, che è il 443). Comunque, speciali setup (che non andiamo ad analizzare) necessitano di eseguire Tomcat su numeri di porta minori di 1024 su molti sistemi operativi. If you change the port number here, you should also change the value specified for the redirectPort attribute on the non-SSL connector. This allows Tomcat to automatically attempt to access a redirect page with users a who security constraint specifying that SSL is required, as required by the Servlet 2.4 Specification. Ci sono opzioni aggiuntive usate per configurare il protocollo SSL. Potrebbe essere necessario aggiungere o cambiare un valore dei seguenti attributi, a seconda di come è stato precedentemente configurato il keystore: Tab. 4.2 – Parametri di configurazione connessione SSL. - 125 - CAP. 4 - TECNOLOGIE SOFTWARE NOME DESCRIZIONE ATTRIBUTO Impostare il valore a true se si vuole che il Tomcat richieda ai client SSL di presentare un Certificato client per poter usare questo clientAuth socket. Impostare il valore want se si vuole che il Tomcat richieda un Certificato client, ma non ritorni errore nel caso non venga presentato. Aggiungere questo attributo se il file del keystore creato non è nel percorso di default che si aspetta Tomcat (un file chiamato .keystore nella cartella user keystoreFile home sotto la quale Tomcat è in esecuzione). Può essere specificato un percorso assoluto, o uno relativo che viene risolto a partire dalla variabile di ambiente $CATALINA_BASE. Aggiungere questo elemento se è stata keystorePass usata una password del keystore (e del certificato) differente da quella che si aspetta Tomcat (changeit). Aggiungere questo elemento se si sta keystoreType usando un keystore PKCS12. I valori possibili per questo attributo sono JKS e PKCS12. - 126 - CAP. 4 - TECNOLOGIE SOFTWARE Il protocollo di crittografia/decrittografia che deve essere usato su questa socket. Non è consigliato cambiare questo valore sslProtocol se si sta utilizzando la JVM di Sun. È stato riportato che nell’implementazione1.4.1 di IBM il protocollo TLS non è compatibile con alcuni diffusi browser. In questo caso, usare il valore SSL. La lista, separata da virgola, di cifre di ciphers crittografia che a questa socket è consentito usare. Di default, qualsiasi cifra disponibile è consentita. L’algoritmo X509 da usare. Il default è impostato all’implementazione Sun algorithm (SunX509). Per le JVM di IBM il valore dovrebbe essere impostato a IbmX509. Per altri fornitori, consultare la documentazione JVM per il valore corretto. truststoreFile Il file di TrustStore da usare per validare i certificate dei client. La password per accedere al TrustStore. Il truststorePass default è impostato al valore dell’attributo keystorePass. Dopo aver completato queste modifiche alla configurazione, è necessario riavviare il Tomcat come si fa - 127 - CAP. 4 - TECNOLOGIE SOFTWARE normalmente, e tutto dovrebbe essere pronto. Dovrebbe essere possibile accedere attraverso SSL a qualsiasi applicazione web supportata da Tomcat. Per esempio, si può provare con: https://localhost:8443 e dovrebbe essere visualizzata la pagina di introduzione del Tomcat (a meno che non sia stato cambiato il ROOT dell’applicazione web). Configurazione della Web Application, file web.xml // Impostazione del keyStore System.setProperty("javax.net.ssl.keyStore","D:/J Telemed/.keystore"); System.setProperty("javax.net.ssl.keyStorePasswor d","changeit"); <security-constraint> <web-resource-collection> <web-resource-name>JTelemedSite</web-resourcename> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> - 128 - CAP. 4 - TECNOLOGIE SOFTWARE <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>CLIENT-CERT</auth-method> <realm-name>JTelemed</realm-name> </login-config> Configurazione del Tomcat, file server.xml <!-- Define a SSL HTTP/1.1 Connector on port 8443 --> <Connector keystoreFile=".keystore" keystorePass="changeit" clientAuth="true" acceptCount="100" disableUploadTimeout="true" enableLookups="false" maxHttpHeaderSize="8192" maxSpareThreads="75" maxThreads="150" minSpareThreads="25" port="8443" scheme="https" secure="true" sslProtocol="TLS"/> - 129 - CAP. 4 - TECNOLOGIE SOFTWARE Configurazione del Web Service, file web.xml <security-constraint> <web-resource-collection> <web-resource-name>JTelemedSite</web-resourcename> <url-pattern>/services/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>CLIENT-CERT</auth-method> <realm-name>JTelemed</realm-name> </login-config> - 130 - CAP. 4 - TECNOLOGIE SOFTWARE 4.4 STRUMENTI DI SVILUPPO 4.4.1 Apache Axis Axis è un progetto della Apache Software Foundation e prende origine dal progetto Apache SOAP: durante lo sviluppo di quest’ultimo è nata l’esigenza di una riscrittura dell’intero progetto per farlo evolvere ad una architettura maggiormente modulare e con un meccanismo di parsing XML di tipo SAX. Axis è un SOAP engine: questo significa che è un framework che si concentra unicamente sulle problematiche della creazione di client e server e per la gestione di messaggi SOAP; in pratica consiste in un insieme di tool per la generazione automatica di classi e in una libreria che “incapsula” in classi Java l’accesso alle tecnologie connesse ai Web Services. L’architettura generale di Axis appare nella seguente figura nei suoi componenti principali. Fig. 4.12 – Architettura generale di Axis. - 131 - CAP. 4 - TECNOLOGIE SOFTWARE Il requestor è un client che effettua una richiesta su alcuni dei protocolli supportati da Axis. Generalmente è usato HTTP. Il requestor può essere una desktop application, una web application, o un altro web service. Il motore Axis agisce agevolando la comunicazione tra client e web service maneggiando la traduzione ad e da web service standard. Axis permette allo sviluppatore di definire una serie di handlers, allacciati alla richiesta o alla risposta. Questi handlers sono simili a filtri servlet; ogni handler svolge uno specifico compito adando avanti fino al prossimo handler in linea. Handlers si adattano insieme in una catena , la quale comprende un set specifico di handlers che corrispondono e richieste o risposte ad un web service. Questo processo è mostrato in figura. Fig. 4.13 – Catena degli handler per le risposte e le richieste. Esempi di handlers sono componenti di sicurezza, sistemi di jogging, e trasformazioni. Un speciale handler , conosciuto come il pivot point handler, esiste per ogni web - 132 - CAP. 4 - TECNOLOGIE SOFTWARE service. Questo handlere elabora le chiamate attuali dei metodi del web service . In altre parole , questo si ha dove il contenuto definito dalla chiamata del metodo come un servizio web è spedito indietro al client richiedente. Le catene di handler sono definite in documento di configurazione usato dal server.config. Questo file motore è un di Axis documento chiamato XML che definisce i parametri di configurazione per Axis. Axis è composto da: Una web application che si occupa di gestire l’ambiente di esecuzione dei servizi (routine, istance poooling, serializzazione e deseralizzazione dei messaggi SOAP, ecc….); Una API composta da classi di utilità per la scrittura di servizi web e da classi necessarie al funzionamento della web application e dei tool; Una seri di tool, tra cui: WSDL2Java per generare scheletri lato server e stub lato client dei servizi web a partire dalla descrizione WSDL; Java2WSDL per generare la descrizione come servizio web di una classe Java; diversi tool per l’amministrazione e la gestione dei servizi installati; - 133 - CAP. 4 - TECNOLOGIE SOFTWARE un TCP monitor stand-alone ed un SOAP monitor integrato nella web application per controllare la forma dei messaggi scambiati tra i servizi nelle fasi di debug e test. Tra le caratteristiche più interessanti di Axis c’è la possibilità di creare web service in maniera immediata a partire da classi Java molto semplici con estensione .jws(Java Web Service). Axis può essere quindi utilizzato in una serie di scenari anche molto diversi tra loro, ad esempio può servire per: la creazione di applicazioni client di servizi web già esistenti per i quali è disponibile il WSDL: utilizzando WSDL2Java si possono creare in maniera automatica gli stub per l’accesso a servizi esistenti implementati con qualsiasi piattaforma utilizzano gli stub . non Le applicazioni necessitano di client ambienti che di esecuzione particolari ma soltanto della presenza della libreria axis.jar nel classpath; la comunicazione via JAX-RPC tra processi Java: le API di Axis implementano una versione JAX-RPC, rendendo possibile lo sviluppo di applicazioni distribuite Java con protocollo di trasporto SOAP; - 134 - CAP. 4 - TECNOLOGIE SOFTWARE la creazione di servizi web a partire da classi java: Axis offre diversi meccanismi per l’implementazione dei servizi. Quello più semplice e completamente trasparente per il programmatore è JWS , ma sono disponibili anche con modelli di servizi più complicati dove è possibile personalizzare , ad esmpio , le modalità di serializzazione dei messaggi , la struttura dei package ed il formato dei parametri , senza mai occuparsi della descrizione WSDL o del formato SOAP dei messaggi , grazie all’integrazione tra l’ambiente di esecuzione e Java2WSDL; l’implementazione di servizi web a partire da descrizioni WSDL: il tool WSDL2Java è particolarmente utile quando si parte dalla descrizione dei servizi per la realizzazione di un sistema piuttosto che della loro implementazione. Tab. 4.3 – WSDL2Java output. JAVA SOURCE FILE DESCRIZIONE L’interfaccia remota che Simple.java descrive il metodo disponibile attraverso questo web service. L’interfaccia Java che SimpleService.java definisce i metodi che ritornano un’istanza di una classe che implementa - 135 - CAP. 4 - TECNOLOGIE SOFTWARE l’interfaccia Simple definita in Simple.java. Una classe di utilità per localizzare, collegare e ritornare una classe che SimpleServiceLocator.java implementa SimpleService.java. Questa è una classe che viene chiamata per collegarsi al web service. Una classe che implementa SimpleSoapBindingStub.java tutti gli attuali dettagli per chiamare il web service dal client. Installazione di Axis Dal sito ufficiale (1) è possibile scaricare il file di installazione. All’interno di questo, oltre alla documentazione, esempi, etc., è presente la cartella axis (sotto la cartella webapps) che contiene tutte le risorse necessarie al funzionamento di Axis. Essa va inserita nel Servlet Engine di cui si dispone; nel nostro casoo utilizzeremo il classico e diffusissimo Apache Tomcat. In questo caso, dunque, la cartella va brutalmente copiata sotto la webapps di Tomcat. In questo modo avremo dotato il nostro server di un Web service engine (di - 136 - CAP. 4 - TECNOLOGIE SOFTWARE fatto una struttura Servlet) in grado di esaudire richieste provenienti da qualsiasi client WS. Serializzazione e deserializzazione Per Axis è facile gestire i Web Services che utilizzano tipi semplici i quali vengono mappati in maniera automatica negli appositi tipi SOAP/WSDL in base alle definizioni di schema XML, XSD (Xml, Schema, Definition). A questo punto occorre soffermarci un momento sul fatto che qualsiasi informazione viaggi sul canale SOAP sia in realtà un documento XML. In questo formato, dunque, deve essere convertito tutto ciò che stia per percorrere il cammino client/server e viceversa. Per quanto concerne gli oggetti necessari come parametri di una invocazione RPC e il corrispettivo valore di ritorno, essi devono appunto essere rappresentati all’interno di una struttura XML. Axis esegue questa operazione per noi, ovvero è in grado di: serializzare un oggetto Java in un blocco XML contenente le medesime informazioni, Serializzare un oggetto Java in un blocco XML contenente le medesime informazioni. Deserializzare un blocco XML rappresentante un dato nel suo omologo oggetto Java. È facile intuire, dunque, con quale ordine vengano eseguite queste operazioni. La serializzazione viene effettuata sugli oggetti che il client invia al service engine come parametri richiesti dal metodo. Il Web service engine - 137 - CAP. 4 - TECNOLOGIE SOFTWARE deserializza tali informazioni XML istanziando gli appositi oggetti Java e valorizzandone i campi. A seguito dell’esecuzione della logica Web service, il valore da restituire subisce anch'esso una serializzazione prima di essere spedito al client sul “canale” SOAP. Infine Il client deserializza il blocco XML restituito rendendo possibile un utilizzo locale dell’oggetto associato. Questa procedura di [de]serializzazione viene eseguita per tutti i tipi di dato, da quelli semplici che Axis gestisce in maniera trasparente, a quelli costituiti da aggregati di tipi semplici, che Axis gestisce in base ad alcune indicazioni che devono essere fornite durante la fase di deployment. - 138 - CAP. 4 - TECNOLOGIE SOFTWARE 4.5 DATABASE RELAZIONALE 4.5.1 MySQL Fig. 4.14 – Struttura di memorizzazione dei database. MySQL è un RDBMS veloce e semplice, usato per i database di molti siti Web. La velocità è stato sin dall’inizio - 139 - CAP. 4 - TECNOLOGIE SOFTWARE l’obiettivo principale degli sviluppatori. Per ottenere maggiore velocità, presero la decisione di offire meno funzionalità rispetto ai maggiori concorrenti (per esempio, Oracle e Sybase). Comunque, anche se MySQL non fornisce funzionalità complete come i suoi concorrenti commerciali, ha tutte le funzionalità necessaria alla maggior parte degli sviluppatori di database. È più facile da installare e da usare rispetto ai suoi concorrenti commerciali, e la differenza nel costo favorisce fortemente MySQL. MySQL è sviluppato, distribuito, e supportato da MySQL AB, che è una compagnia Svedese. Questa fornisce due tipi di licenze: • Open source software: MySQL è disponibile con licenza GNU GPL (General Public License) per scopi non commerciali. Chiunque soddisfi le richieste della GPL può usare il software liberamente. Se si usa MySQL per un database in un sito Web, MySQL può essere usato gratuitamente, anche se il sito Web è di tipo commerciale. • Commercial license: MySQL è disponibile con licenza commerciale per quelli che la preferiscono al GPL. Se uno sviluppatore vuole usare MySQL come parte di un nuovo prodotto software e vuole vendere questo prodotto, invece di rilasciarlo sotto licenza GPL, lo sviluppatore deve acquistare una licenza commerciale, ad un costo ragionevole. - 140 - CAP. 4 - TECNOLOGIE SOFTWARE Trovare il supporto tecnico per MySQL non è un problema. Ci si può iscrivere ad una degli innumerovoli forum di discussione offerti anche sul sito web di MySQL, www.mysql.com. È possibile consultare anche uno degli archivi di e-mail, che contengono una ampia base di conoscenza su MySQL con domande e risposte comuni. Se si vuole un supporto di tipo commerciale, MySQL AB offrer contratti di supporto tecnico — cinque livelli di supporto, dal supporto diretto con e-mail a quello telefonico, a cinque livelli di prezzo. Vantaggi di MySQL MySQL è un database popolare tra gli sviluppatori Web. La sua velocità e leggerezza lo rendono ideale per un sito Web. Aggiungendo a questo il fatto che è open source, il che significa gratis, si ha la spiegazione della sua popularità. Vediamo alcuni dei principali vantaggi: • Velocità. Il principale obiettivo delle persone che hanno sviluppato MySQL era la velocità. Conseguentemente, il software, sin dall’inizio, è stato progettato con la velocità in mente. • Basso costo. MySQL è gratuito sotto la licenza open source GPL, e il costo di una licenza commerciale è ragionevole. - 141 - CAP. 4 - TECNOLOGIE SOFTWARE • Facilità d'uso. È possibile costruire un database MySQL ed interagire con esso usando poche semplici istruzioni nel linguaggio SQL, che è lo standard per la comunicazione con i RDBMS. • Supporto sistemi operativi. MySQL gira su una gran numero di sistemi operavi — Windows, Linux, Mac OS, molte varianti di Unix (incluse Solaris, AIX, e DEC Unix), FreeBSD, OS/2, Irix, e altri. • Supporto Tecnico. Il supporto tecnico è disponibile. Un gran numero di utenti offre un supporto gratuito attraverso forum di discussione. Gli stessi sviluppatori di MySQL partecipano a questi forum e sono contattabili via e-mail. È possibile ottenere anche il supporto tecnico diretto da MySQL AB ad un prezzo contenuto. • Sicurezza. Il sistema flessibile di autorizzazioni di MySQL permette di concedere diversi privilegi sul database (per esempio, il diritto di creare un database o cancellare dati) agli specifici utenti o gruppi di utenti. Le password sono criptate. • Supporto grandi databasi. MySQL può gestire fino a 50 milioni di record e più. Il limite predefinito di dimensione per una tabella è 4GB, ma è possibile aumentarlo (se il - 142 - CAP. 4 - TECNOLOGIE SOFTWARE sistema operativo lo consente) ad un limite teorico di 8 milioni di terabyte (TB). • Personalizzabilità. La licenza open source GPL permette ai programmatori di modificare il software di MySQL per adattarlo alle proprie esigenze. Come lavora MySQL Il software di MySQL è composto dal server MySQL, svariati programmi di utilità che aiutano nell'amministrazione dei database MySQL ed alcuni che software di supporto del quale il server MySQL ha bisogno (ma che non è necessario conoscere). Il nucleo del sistema è il server MySQL. Il server MySQL è il gestore del sistema di database. Gestisce tutte le istruzioni riguardanti i database. Per esempio, se si vuole creare un database nuovo, basta inviare un messaggio al server MySQL che dice “crea un nuovo database e chiamalo newdata.” Il server MySQL quindi crea una sottocartella nella sua cartella dati, e la chiama newdata, e vi mette i file necessari nei formati corretti. Allo stesso modo, per aggiungere dati a quel database, basta inviare un messaggio al server MySQL, fornendogli i dati e dicendo dove questi vanno aggiunti. Prima di poter passare le istruzioni al server MySQL , questo deve essere operativo ed in attesa di richieste. Il server MySQL è solitamente impostato in modo da avviarsi - 143 - CAP. 4 - TECNOLOGIE SOFTWARE all’avvio del computer e rimanere in esecuzione fino al suo spegnimento. Questa è la configurazione comune per un sito Web. Comunque, non è necessario impostare l’avvio in questo modo. Se necessario, è possibile avviarlo manualmente ogni qualvolta sia richiesto accedere ad un database. Quando è in esecuzione, il server MySQL resta in ascolto ed attende che gli arrivino dei messaggi contenenti richieste. Communicazione con il server MySQL Tutte le interazioni con il database vengono effettuate passando i messaggi al server MySQL. Si possono mandare messaggi al server MySQL in svariati modi, a seconda del linguaggio e della piattaforma utilizzata. Ogni linguaggio ha delle dichiarazioni specifiche che vengono usate per inviare istruzioni al server MySQL. Il server MySQL deve essere in grado di comprendere le istruzioni che gli vengono inviate. Si comunica utilizzando l’SQL (Structured Query Language), che è un linguaggio standard utilizzato dalla maggior parte dei RDBMS. Il server MySQL comprende l’SQL. Dalla parte Java, il driver JDBC non conosce l’SQL, si limita a stabilire la connessione con il server MySQL e invia il messaggio SQL sulla connessione. Il server MySQL interpreta il messaggio SQL ed esegue le istruzioni, a questo punto risponde con un messaggio di ritorno, notificando il suo stato e che cosa ha fatto (o - 144 - CAP. 4 - TECNOLOGIE SOFTWARE ritornando un errore se non è in grado di capire o eseguire il le istruzioni). Per una descrizione più approfondita della sintassi del linguaggio SQL basta consultare uno dei manuali in materia. - 145 - CONCLUSIONI CONCLUSIONI Lo scopo del progetto, era quello di creare una struttura software flessibile che offrisse servizi di teleconsulto e teleassistenza distinguendo in modo inequivocabile la fase di effettuazione dell’esame dalla fase di refertazione. L’architettura che è stata realizzata è in grado di fornire un valido supporto anche per altri servizi tipici di applicazioni di telemedicina. Prima di iniziare lo sviluppo di nuove funzionalità è necessaria una fase di test per consolidare il sistema e renderlo efficiente e cominciare ad utilizzarlo all’interno dell’azienda sanitaria per evidenziarne eventuali limiti. In prospettiva futura con l’avvento del documento di identità digitale è auspicabile che i servizi disponibili solo a livello del personale sanitario, vengano messi a disposizione direttamente per l’utente finale: il cittadino. L’obiettivo futuro sarà sfruttare la grande flessibilità con sui è stato progettato MiRo, espandendo le sue funzionalità per poter raggiungere direttamente gli utenti finali, i pazienti, in modo da rendere il servizio sanitario efficiente e capillare. - 146 - CONCLUSIONI Il progetto MiRo è stato presentato alla 2nd Conferenza Internazionale EMMIT 2006: EURO-MEDITERRANEAN MEDICAL INFORMATICS TELEMEDICINE. MiRo: A Virtual Private Network For Telehealth Telehealth Services ROBERTO DI ROSA, MIRCO STURARI, ALDO FRANCO DRAGONI*, GIUSEPPE GIAMPIERI** *DEIT, Dipartimento di Elettronica Intelligenza Artificiale e Telecomunicazioni – Università Politecnica delle Marche, Ancona **ASUR Zona 7, Azienda Sanitaria Locale delle Marche Ancona 1 MiRo Project • CONTEXT software-design to organize telemedicine services • MOTIVATIONS necessity to integrate sanitary services in a wide and delocalized environment • SCOPE to provide a flexible architecture for teleconsulting and telereporting 2 - 147 - and CONCLUSIONI Telemedicine represents the erogation of sanitary service at distances through informatics and telecommunications technologies 3 Telemedicine ensures high quality digital information even for complex health services 4 - 148 - CONCLUSIONI Telemedicine reaches better results if coordinated with the other applications 5 MiRo - Architecture is configured as a substrate to integrate sanitary systems 6 - 149 - CONCLUSIONI MiRo - Finalities • provides a flexible software framework for teleconsulting and services development • integrates with the existing health information system to facilitate and rationalize information interchange • improves and facilitates TCP/IP-based teleconsulting services in Intra/Internet environment • provides a report and “second opinion” service 7 Abstract Data-Structure Data Data-Structure • Exam-data: generic digital data produced by an examination • Report: text-file and digital signature • Event: database record containing – the date-time of the insertion in the database – the examination type – the laboratory who produced it - 150 - 8 CONCLUSIONI MiRo - Architecture DATA-STRUCTURE • Data: digital data produced by an examination • Event: database record that links to the exam SYSTEM-ENTITIES • Repository: archives event • Laboratory: generates event • Phisician: reports examination • Report: text-file containing report 9 Repository • Allows laboratories to generate and manage events • Allows phisicians to control and report examination • Stores reports allowing teleconsultation ¾Acts as a sophisticated DBMS for event-records 10 - 151 - CONCLUSIONI Laboratory • Generates clinical event • Provides digital data of examination • Monitors the workflow of reporting phase • Terminates reporting phase 11 Phisician • Consults published examination • Consults reports written by other phisicians • Compiles and signs report • Send this report to a timestamp service and then to the repository 12 - 152 - CONCLUSIONI From Exam to Report 1. Laboratory executes an examination that produces digital data 2. Laboratory pubblicates exam through the creation of an event 3. Phisician compiles and signs a report and sends it to the timestamp service 4. Timestamp service appends a certificated datetime to the report and forwards this back to the phisician 5. Phisician sends this composite-data to the Repository 6. The Laboratory decides when to terminate the refertation process 13 Event-Generation Process Event Event-Generation 1. Laboratory executes an examination that produces digital data 2. Laboratory pubblicates exam through the creation of an event - 153 - 14 CONCLUSIONI Event-Generation Process Event Event-Generation 3. Repository records event informations about exam 4. The exam is reachable through the MiRo Network from each authorized client 15 Report-Generation Process Report Report-Generation 1. Phisician, as client, connects to Repository and can see a list of events 2. Phisician chooses an event and retrieves linked examination data 16 - 154 - CONCLUSIONI Report-Generation Process Report Report-Generation 3. Phisician sees reports written by other associates 4. Phisician compiles a report and sends it to the Repository 17 MiRo - Technologies • Service-Oriented Architecture based on common standards widely applied in different sanitary contexts • implemented through Web Services (SOAP, XML, …), which are becoming a standard to distribute services inside an heterogeneous environment such as Internet • Client can enter in any moment to consult and to insert event or report data using a common Web Browser • transactions are encrypted through standard SSL X509.v3, using standard Digital Certificates 18 - 155 - CONCLUSIONI MiRo – Development Environment • Java 2 Platform Standard Edition • development tools: – Eclipse 3.1 – as development environment – Apache Axis and Java Servlet – to realize web services – Apache Struts and Java Sever Pages – to design web interfaces • Java web services technology – JDBC interface (in our case MySQL). • Clients adopt just Web Browsers 19 MiRo - Security Layer • Secure Socket Layer(SSL) – Confidentiality achieved through cryptography, guarantees that data can exclusively be read from the authorized user. • Digital Sign – Integrity and no-repudiation, achieved through the digital sign, guarantees that information are not modified during the transmission and that those who use the system assume the responsibilities of what they are doing. – Authentication achieved through digital certificate, Username and Password, guarantees that the user is exactly the person he declares to be. 20 - 156 - CONCLUSIONI Conclusions and future works • MiRo is a software architecture for TCP/IP-based telereporting activities and “second opinion” services • thanks to its flexibility, can be exploited in many different operational realities and is able to incorporate different Intra/Internet-based services. • In future perspective, we foresee the possibility to implement and to provide new services and to reach citizens. 21 Greetings • ALDO FRANCO DRAGONI DEIT, Dipartimento di Elettronica Intelligenza Artificiale e Telecomunicazioni – Università Politecnica delle Marche, Ancona • GIUSEPPE GIAMPIERI ASUR Zona 7, Azienda Sanitaria Locale delle Marche Ancona THANK FOR YOUR ATTENTION 22 - 157 - CONCLUSIONI Ringraziamo per la collaborazione il personale dell’ASUR Marche Zona 7 e in particolar modo: Giuseppe Giampieri, Fabio Tonucci, Sergio Piersantelli ed Ebe Tartufo. Un ringraziamento al professore Paolo Puliti per la disponibilità e per i consigli nella scelta della tesi. Un ringraziamento speciale al professore Aldo Franco Dragoni per il sostegno, l’appoggio e per la collaborazione fornita nello sviluppo dell’intero progetto. Un saluto particolare al mio amico e collega Roberto Di Rosa che ha sviluppato con me questo progetto, aiutandomi nei momenti di difficoltà incontrati in questi mesi di lavoro. Un grazie anche alla mia famiglia e a tutti gli amici che non mi hanno fatto mai mancare il loro affetto, in special modo Francesco, Michel, Paolo, Giorgio, Silvia, Marco, Cristina, Matteo, ai miei colleghi di lavoro Marco Pela e Massimo Santarelli della Siint, ai miei compagni di ballo latino-americano e di calcetto che mi fanno divertire. Mirco Sturari - 158 - APPENDICE - CODICE SORGENTE APPENDICE CODICE SORGENTE Codice Java scritto per l’implementazione dei Web Service dal lato server. A.1 Common Library A.1.1 Application.java A.1.2 Attachment.java A.1.3 Certificate.java A.1.4 Client.java A.1.5 DateTime.java A.1.6 FileUtil.java A.1.7 ServletUtil.java A.1.8 StringUtil.java A.2 Web Service Repository A.2.1 WSRepository.java A.3 Web Service Laboratory A.3.1 WSLaboratory.java - 159 - APPENDICE - CODICE SORGENTE A.1 COMMON LIBRARY A.1.1 Application.java package it.jtelemed.common; import java.sql.*; public class Application { public static final String EVT_STATO_APERTO = "0"; public static final String EVT_STATO_REFERTO = "1"; public static final String EVT_STATO_CHIUSO = "2"; private private private private private private private String String String String String String String url = ""; user = ""; password = ""; driverClass = ""; archivePath = ""; reportPath = ""; welcomeMsg = ""; /** * Ritorna l'url per l'accesso al database * */ public String getUrl() { return url; } /** * Imposta l'url per l'accesso al database * */ public void setUrl(String name) { url = name; } /** * Ritorna il nome utente per l'accesso al database * */ public String getUser() { return user; - 160 - APPENDICE - CODICE SORGENTE } /** * Imposta il nome utente per l'accesso al database * */ public void setUser(String name) { user = name; } /** * Ritorna la password per l'accesso al database * */ public String getPassword() { return password; } /** * Imposta la password per l'accesso al database * */ public void setPassword(String name) { password = name; } /** * Ritorna la classe java del driver jdbc * */ public String getDriverClass() { return driverClass; } /** * Imposta la classe java del driver jdbc * */ public void setDriverClass(String name) { driverClass = name; } /** * Ritorna il percorso di archiviazione degli allegati * */ public String getArchivePath() { return archivePath; - 161 - APPENDICE - CODICE SORGENTE } /** * Imposta il percorso di archiviazione degli allegati * */ public void setArchivePath(String name) { archivePath = name; } /** * Ritorna il percorso di archiviazione dei referti * */ public String getReportPath() { return reportPath; } /** * Imposta il percorso di archiviazione dei referti * */ public void setReportPath(String name) { reportPath = name; } /** * Ritorna il messaggio di benvenuto * */ public String getWelcomeMsg() { return welcomeMsg; } /** * Imposta il messaggio di benvenuto * */ public void setWelcomeMsg(String name) { welcomeMsg = name; } /** * Ritorna la stringa di connessione JDBC * */ public String getJdbcConnectionString() { - 162 - APPENDICE - CODICE SORGENTE String jdbcConnStr = ""; jdbcConnStr = url; if (user.compareTo("") != 0) jdbcConnStr += "?user=" + user + "&pass=" + password; return jdbcConnStr; } /** * Ritorna un riferimento ad un oggetto java.sql.Connection * */ public java.sql.Connection getDbConnection() { try { Class.forName(driverClass); return java.sql.DriverManager.getConnection(getJdbcConnectionStrin g()); } catch (java.sql.SQLException ex) { return null; } catch (ClassNotFoundException ex) { return null; } } /** * Ritorna le informazioni dell'utente con il CommonName specificato * * @param utn_cn CommonName dell'utente * * @return elenco delle informazioni dell'utente */ public String getUserInfo(String utn_cn) { String info = ""; try { Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT utn_id, utn_type, utn_labwslink, utn_archivepath FROM tm_utenti WHERE utn_cn = " + StringUtil.quoteString(utn_cn)); if (rs.next()) { - 163 - APPENDICE - CODICE SORGENTE info = rs.getInt(1) + ";" + rs.getString(2) + ";" + rs.getString(3) + ";" + rs.getString(4); } rs.close(); stmt.close(); conn.close(); } catch (SQLException ex) { info = ""; } catch (Exception ex) { info = ""; } return info; } /** * Ritorna l'elenco degli eventi * che il dottore è in grado di visualizzare/refertare * che il laboratorio è in grado di visualizzare/aprire * * @param utn_id Identificativo dell'utente * * @return elenco degli eventi "evt_id;evt_dtmevento;uer_descr;evt_token;evt_wslink;evt_st ato|" */ public String selectEvents(int utn_id) { StringBuilder ret = new StringBuilder(); try { // Formazione della select per sapere quali esami vedere String sql_viewexams = "(SELECT prm_trf_codice FROM tm_permissioni WHERE prm_utn_id = " + utn_id + ")"; Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT evt_id, evt_dtmevento, uer_descr, evt_token, evt_wslink, evt_stato, trf_codice, trf_descr FROM tm_eventi LEFT JOIN tm_tariffario ON tm_eventi.evt_trf_codice = tm_tariffario.trf_codice LEFT JOIN tm_unitaeroganti ON tm_eventi.evt_codente = tm_unitaeroganti.uer_codente AND tm_eventi.evt_codstruttura = tm_unitaeroganti.uer_codstruttura AND tm_eventi.evt_codspecialita = - 164 - APPENDICE - CODICE SORGENTE tm_unitaeroganti.uer_codspecialita AND tm_eventi.evt_codunitaerogante = tm_unitaeroganti.uer_codunitaerogante WHERE evt_trf_codice IN " + sql_viewexams + " ORDER BY evt_id DESC"); while (rs.next()) { ret.append((ret.length() == 0?"":"§")); ret.append(rs.getInt(1) + ";"); ret.append(rs.getString(2) + ";"); ret.append(rs.getString(3) + ";"); ret.append(rs.getString(4) + ";"); ret.append(rs.getString(5) + ";"); ret.append(rs.getString(6) + ";"); ret.append(rs.getString(7) + ";"); ret.append(rs.getString(8)); } rs.close(); stmt.close(); conn.close(); return(ret.toString()); } catch (SQLException ex) { return(""); } catch (Exception ex) { return(""); } } /** * Inserisce un nuovo evento con i dati specificati * Imposta lo stato dell'evento a APERTO * * @param utn_id Identificativo dell'utente * @param evt_codice Codice dell'esame da Tariffario * @param evt_esterno evt_esterno * @param evt_token evt_token * @param evt_wslink evt_wslink * @param evt_richiedente evt_richiedente * @param evt_impegnativa evt_impegnativa * @param evt_note evt_note * * @return evt_id Identificativo dell'evento */ public int openEvent(int utn_id, String evt_codice, boolean evt_esterno, String evt_token, String evt_wslink, String evt_richiedente, String evt_impegnativa, String evt_note) { Connection conn = null; Statement stmt = null; String utn_codente = ""; - 165 - APPENDICE - CODICE SORGENTE String utn_codstruttura = ""; String utn_codspecialita = ""; String utn_codunitaerogante = ""; int evt_id = -1; try { // Lettura dei dati dell'utente conn = getDbConnection(); stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT utn_codente,utn_codstruttura,utn_codspecialita,utn_codunita erogante FROM tm_utenti WHERE utn_id = " + utn_id); if (rs.next()) { utn_codente = rs.getString(1); utn_codstruttura = rs.getString(2); utn_codspecialita = rs.getString(3); utn_codunitaerogante = rs.getString(4); } rs.close(); stmt.close(); // Inserimento del record nella tabella del repository stmt = conn.createStatement(); stmt.execute("INSERT INTO tm_eventi (evt_trf_codice, evt_codente, evt_codstruttura, evt_codspecialita, evt_codunitaerogante, evt_esterno, evt_dtmevento, evt_token, evt_wslink, evt_stato, evt_richiedente, evt_impegnativa, evt_note) VALUES (" + StringUtil.quoteString(evt_codice) + "," + StringUtil.quoteString(utn_codente) + "," + StringUtil.quoteString(utn_codstruttura) + "," + StringUtil.quoteString(utn_codspecialita) + "," + StringUtil.quoteString(utn_codunitaerogante) + "," + StringUtil.quoteString((evt_esterno?"1":"0")) + "," + StringUtil.quoteString(DateTime.getCurrentDateTime()) + "," + StringUtil.quoteString(evt_token) + "," + StringUtil.quoteString(evt_wslink) + "," + StringUtil.quoteString(EVT_STATO_APERTO) + "," + - 166 - APPENDICE - CODICE SORGENTE StringUtil.quoteString(evt_richiedente) + "," + StringUtil.quoteString(evt_impegnativa) + "," + StringUtil.quoteString(evt_note) + ")"); stmt.close(); conn.close(); evt_id = getEventID(evt_wslink, evt_token); } catch (SQLException ex) { evt_id = -1; } catch (Exception ex) { evt_id = -1; } return(evt_id); } /** * Aggiorna lo stato dell'evento a REFERTATO * * @param evt_id Identificativo dell'evento * * @return vero se l'evento è stato aggiornato con successo */ public boolean updateEvent(int evt_id) { boolean updated = false; try { Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); stmt.execute("UPDATE tm_eventi SET evt_stato = " + StringUtil.quoteString(EVT_STATO_REFERTO) + " WHERE evt_stato <> " + StringUtil.quoteString(EVT_STATO_CHIUSO) + " AND evt_id = " + evt_id); stmt.close(); conn.close(); updated = true; } catch (SQLException ex) { updated = false; } catch (Exception ex) { updated = false; } return updated; } - 167 - APPENDICE - CODICE SORGENTE /** * Aggiorna lo stato dell'evento a CHIUSO * Aggiorna la data di chiusura a NOW() * * @param evt_id Identificativo dell'evento * * @return vero se l'evento è stato aggiornato con successo */ public boolean closeEvent(int evt_id) { boolean updated = false; try { Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); stmt.execute("UPDATE tm_eventi SET evt_stato = " + StringUtil.quoteString(EVT_STATO_CHIUSO) + ", evt_dtmchiuso = " + StringUtil.quoteString(DateTime.getCurrentDateTime()) + " WHERE evt_stato <> " + StringUtil.quoteString(EVT_STATO_CHIUSO) + " AND evt_id = " + evt_id); stmt.close(); conn.close(); updated = true; } catch (SQLException ex) { updated = false; } catch (Exception ex) { updated = false; } return updated; } /** * Ritorna il evt_id dell'evento individuato da evt_wslink e evt_token * * @param evt_wslink wslink della risorsa collegata all'evento * @param evt_token token della risorsa collegata all'evento * * @return evt_id Identificativo dell'evento */ public int getEventID(String evt_wslink, String evt_token) { int evt_id = -1; try { Connection conn = getDbConnection(); - 168 - APPENDICE - CODICE SORGENTE Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT evt_id FROM tm_eventi WHERE evt_wslink LIKE " + StringUtil.quoteString(evt_wslink) + " AND evt_token LIKE " + StringUtil.quoteString(evt_token)); if (rs.next()) { evt_id = rs.getInt(1); } rs.close(); stmt.close(); conn.close(); } catch (SQLException ex) { evt_id = -1; } catch (Exception ex) { evt_id = -1; } return evt_id; } /** * Ritorna il ref_ind del referto per l'evento individuato da evt_id * * @param evt_id Identificativo dell'evento * * @return ref_ind Indice del referto */ public int getFreeReportInd(int evt_id) { int ref_ind = 1; try { Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT MAX(ref_ind) FROM tm_referti WHERE ref_evt_id = " + evt_id); if (rs.next()) { ref_ind = rs.getInt(1) + 1; } rs.close(); stmt.close(); conn.close(); } catch (SQLException ex) { ref_ind = 1; } catch (Exception ex) { ref_ind = 1; } - 169 - APPENDICE - CODICE SORGENTE return ref_ind; } /** * Allega tutti i referti dell'evento individuato da evt_id * presenti nella tabella tm_referti * recuperandoli dalla cartella dei report * * @param evt_id Identificativo dell'evento * * @return elenco dei referti "ref_ind;ref_filename;ref_timestamp;ref_notvalid;utn_cn|" */ public String getReportFiles(int evt_id) { StringBuilder ret = new StringBuilder(); try { Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT ref_ind, ref_filename, ref_timestamp, ref_notvalid, utn_cn FROM tm_referti LEFT JOIN tm_utenti ON tm_referti.ref_utn_id = tm_utenti.utn_id WHERE ref_evt_id = " + evt_id + " ORDER BY ref_ind"); while (rs.next()) { String report_name = FileUtil.parsePathName(getReportPath()) + rs.getString(2); String digest_name = FileUtil.parsePathName(getReportPath()) + FileUtil.getReportFileName("digest", "bin", evt_id, rs.getInt(1)); // Verifica la firma del file contenente il referto // prima di allegarlo alla risposta del web service if (FileUtil.verifySignedFile(report_name, digest_name)) { if (Attachment.addAttachmentPart(ServletUtil.getSOAPResponseMe ssage(), FileUtil.parsePathName(getReportPath()) + rs.getString(2))) { ret.append((ret.length() == 0?"":"§")); ret.append(rs.getInt(1) + ";"); ret.append(rs.getString(2) + ";"); - 170 - APPENDICE - CODICE SORGENTE ret.append(rs.getString(3) + ";"); ret.append(rs.getString(4) + ";"); ret.append(rs.getString(5)); } } } rs.close(); stmt.close(); conn.close(); } catch (SQLException ex) { ret.append(ex.toString()); } catch (Exception ex) { ret.append(ex.toString()); } // Ritorno la lista di tutti i referti allegati return ret.toString(); } /** * Carica il referto per l'evento individuato da evt_id * * @param evt_id Identificativo dell'evento * @param utn_id Identificativo dell'utente * @param rep_content Contenuto del referto * * @return vero se il referto è stato salvato con successo */ public boolean uploadReport(int evt_id, int utn_id, String rep_content) { boolean uploaded = false; try { int ref_ind = getFreeReportInd(evt_id); String ref_filename = FileUtil.getReportFileName("report", "txt", evt_id, ref_ind); String dig_filename = FileUtil.getReportFileName("digest", "bin", evt_id, ref_ind); // Salvataggio del referto come file if (FileUtil.writeFileBuffer(FileUtil.parsePathName(reportPath ) + ref_filename, rep_content.getBytes())) { // Inserimento del record del referto - 171 - APPENDICE - CODICE SORGENTE Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); stmt.execute("INSERT INTO tm_referti (ref_evt_id, ref_notvalid, ref_filename, ref_timestamp, ref_utn_id) VALUES (" + evt_id + ",'0'," + StringUtil.quoteString(ref_filename) + "," + StringUtil.quoteString(DateTime.getCurrentDateTime()) + "," + utn_id + ")"); stmt.close(); conn.close(); javax.xml.soap.AttachmentPart att = null; // Leggo il digest firmato in allegato att = Attachment.getAttachmentPart(ServletUtil.getSOAPResponseMes sage(), 0); if (att != null) { // Salvo il digest firmato in allegato if (Attachment.saveAttachment(att, dig_filename)) uploaded = true; } } } catch (SQLException ex) { uploaded = false; } catch (Exception ex) { uploaded = false; } return uploaded; } /** * Dichiara come non valido un determinato referto * dell'evento individuato da evt_id * * @param evt_id Identificativo dell'evento * @param ref_ind Indice del referto * * @return vero se il referto è stato aggiornato con successo */ public boolean invalidateReport(int evt_id, int ref_ind) { boolean updated = false; try { - 172 - APPENDICE - CODICE SORGENTE Connection conn = getDbConnection(); Statement stmt = conn.createStatement(); stmt.execute("UPDATE tm_referti SET ref_notvalid = '1' WHERE ref_evt_id = " + evt_id + " AND ref_ind = " + ref_ind); stmt.close(); conn.close(); updated = true; } catch (SQLException ex) { updated = false; } catch (Exception ex) { updated = false; } return updated; } /** * Allega il file di referto al messaggio di risposta SOAP * * @param filename nome del file di referto * * @return vero se la risorsa è stata allegata con successo */ public boolean getEventFile(String filename) { boolean updated = false; try { updated = Attachment.addAttachmentPart(ServletUtil.getSOAPResponseMes sage(), FileUtil.parsePathName(archivePath) + filename); } catch (Exception ex) { updated = false; } return updated; } } A.1.2 Attachment.java package it.jtelemed.common; public class Attachment { - 173 - APPENDICE - CODICE SORGENTE /** * Aggiunge un nuovo allegato al messaggio SOAP * * @param mSoapMsg messaggio SOAP * @param path file da allegare * * @return true se il file è stato allegato */ public static boolean addAttachmentPart(javax.xml.soap.SOAPMessage mSoapMsg, String path) { javax.activation.DataHandler dh; try { // Creo il FileDataSource per leggere il file javax.activation.FileDataSource fds = new javax.activation.FileDataSource(path); // Creo il DataHandler per inviare il file dh = new javax.activation.DataHandler(fds); } catch (Exception ex) { dh = null; } return addAttachmentPart(mSoapMsg, dh); } /** * Aggiunge un nuovo allegato al messaggio SOAP * * @param mSoapMsg messaggio SOAP * @param dh dati da allegare * * @return true se il file è stato allegato */ public static boolean addAttachmentPart(javax.xml.soap.SOAPMessage mSoapMsg, javax.activation.DataHandler dh) { try { if (mSoapMsg != null && dh != null) { // Crea un nuovo allegato a partire dal DataHandler javax.xml.soap.AttachmentPart att = null; att = createAttachmentPart(mSoapMsg, dh); if (att != null) { mSoapMsg.addAttachmentPart(att); - 174 - APPENDICE - CODICE SORGENTE return true; } } } catch (Exception ex) { } return false; } /** * Aggiunge un nuovo allegato al messaggio SOAP * * @param stub client Axis * @param path file da allegare * * @return true se il file è stato allegato */ public static boolean addAttachmentPart(org.apache.axis.client.Stub stub, String path) { javax.activation.DataHandler dh; try { // Creo il FileDataSource per leggere il file javax.activation.FileDataSource fds = new javax.activation.FileDataSource(path); // Creo il DataHandler per inviare il file dh = new javax.activation.DataHandler(fds); } catch (Exception ex) { dh = null; } return addAttachmentPart(stub, dh); } /** * Aggiunge un nuovo allegato al messaggio SOAP * * @param stub client Axis * @param dh dati da allegare * * @return true se il file è stato allegato */ public static boolean addAttachmentPart(org.apache.axis.client.Stub stub, javax.activation.DataHandler dh) { try { if (stub != null && dh != null) { - 175 - APPENDICE - CODICE SORGENTE // Crea un nuovo allegato a partire dal DataHandler stub.addAttachment(dh); return true; } } catch (Exception ex) { } return false; } /** * Crea un nuovo allegato al messaggio SOAP * * @param mSoapMsg messaggio SOAP * @param String path file da allegare * * @return true se è stato creato l'allegato */ public static javax.xml.soap.AttachmentPart createAttachmentPart(javax.xml.soap.SOAPMessage mSoapMsg, String path) { javax.activation.DataHandler dh; try { // Creo il FileDataSource per leggere il file javax.activation.FileDataSource fds = new javax.activation.FileDataSource(path); // Creo il DataHandler per inviare il file dh = new javax.activation.DataHandler(fds); } catch (Exception ex) { dh = null; } if (dh != null) return createAttachmentPart(mSoapMsg, dh); else return null; } /** * Crea un nuovo allegato al messaggio SOAP * * @param mSoapMsg messaggio SOAP * @param dh dati da allegare * * @return true se è stato creato l'allegato */ - 176 - APPENDICE - CODICE SORGENTE public static javax.xml.soap.AttachmentPart createAttachmentPart(javax.xml.soap.SOAPMessage mSoapMsg, javax.activation.DataHandler dh) { try { if (mSoapMsg != null) { // Crea un nuovo allegato a partire dal DataHandler javax.xml.soap.AttachmentPart att = (javax.xml.soap.AttachmentPart)mSoapMsg.createAttachmentPar t(dh); return att; } } catch (Exception ex) { } return null; } /** * Ritorna il numero di allegati al messaggio SOAP * * @param mSoapMsg messaggio SOAP * * @return numero di allegati */ public int countAttachments(javax.xml.soap.SOAPMessage mSoapMsg) { int count = -1; try { if (mSoapMsg != null) count = mSoapMsg.countAttachments(); } catch (Exception ex) { count = -1; } return count; } /** * Ritorna l'allegato al messaggio SOAP * * @param mSoapMsg messaggio SOAP * @param index indice dell'allegato * * @return allegato al messaggio SOAP */ public static javax.xml.soap.AttachmentPart getAttachmentPart(javax.xml.soap.SOAPMessage mSoapMsg, int index) { - 177 - APPENDICE - CODICE SORGENTE javax.xml.soap.AttachmentPart att = null; try { if (mSoapMsg != null && mSoapMsg.countAttachments() > 0 && index > -1 && index < mSoapMsg.countAttachments()) { // Recupera l'Iterator per gli allegati java.util.Iterator attIter = mSoapMsg.getAttachments(); int i = 0; while (attIter.hasNext()) { att = (javax.xml.soap.AttachmentPart)attIter.next(); // Prendo l'allegato if (i == index) break; i++; } } } catch (Exception ex) { // } return att; } /** * Ritorna gli allegati al messaggio SOAP dello stub * * @param stub client Axis * * @return allegati al client Axis */ public static Object[] getAttachments(org.apache.axis.client.Stub stub) { Object[] atts = null; try { if (stub != null) { // Recupera l'Iterator per gli allegati atts = stub.getAttachments(); } } catch (Exception ex) { // } return atts; } /** * Salva l'allegato al messaggio SOAP - 178 - APPENDICE - CODICE SORGENTE * * @param att allegato da salvare * @param path nome del file da salvare * * @return true se il file è stato salvato */ public static boolean saveAttachment(javax.xml.soap.AttachmentPart att, String path) { boolean saved = false; try { byte[] buf = null; buf = FileUtil.readFileBuffer(att); FileUtil.writeFileBuffer(path, buf); saved = true; } catch (Exception ex) { saved = false; } return saved; } } A.1.3 Certificate.java package it.jtelemed.common; import java.security.cert.X509Certificate; import javax.servlet.http.HttpServletRequest; public final class Certificate { /** * Ritorna l'elenco dei certificati X509 nella request * * @param request oggetto richiesta della Servlet * * @return elenco dei certificati X509 */ public static X509Certificate[] getX509Certificates(HttpServletRequest request) { X509Certificate[] certs = null; try { if (request.isSecure()) - 179 - APPENDICE - CODICE SORGENTE { if (request.getAttribute("javax.servlet.request.X509Certificat e") != null) { // Leggo dagli attributi dell'oggetto request // l'elenco dei certificati certs = (java.security.cert.X509Certificate[])request.getAttribute( "javax.servlet.request.X509Certificate"); } } } catch (Exception ex) { certs = null; } return certs; } /** * Ritorna il certificato X509 i-esimo nella request * * @param request oggetto richiesta della Servlet * * @return certificato X509 i-esimo */ public static X509Certificate getX509Certificate(HttpServletRequest request, int index) { X509Certificate[] certs = null; try { certs = getX509Certificates(request); if (certs != null && index > -1 && index < certs.length) return certs[index]; } catch (Exception ex) { } return null; } /** * Ritorna il SubjectDN del 1° certificato X509 nella request * * @param request oggetto richiesta della Servlet * * @return elenco dei certificati X509 */ - 180 - APPENDICE - CODICE SORGENTE public static java.security.Principal getSubjectDN(HttpServletRequest request) { java.security.Principal subjDN = null; try { X509Certificate[] certs = getX509Certificates(request); if (certs != null && certs.length > 0) { subjDN = certs[0].getSubjectDN(); } } catch (Exception ex) { subjDN = null; } return subjDN; } /** * Estrae un campo dal un DistinguishName, ad es. CN * * @param certDN DistinguishName * @param fieldDN Nome del Campo * * @return Valore del Campo * */ public static String extractDNField(java.security.Principal certDN, String fieldDN) { // dn_string contiene il DistinguishName String dn_string = certDN.toString(); // dn_field_value conterrà il Valore del Campo, ad es. Mirco Sturari String dn_field_value = ""; // dn_fields[] contiene il DistinguishName spezzato dalle virgole String[] dn_fields = dn_string.split(","); // dn_fields conterrà uno degli elementi di dn_fields[] String dn_field = ""; // dn_token conterrà il token dell'elemento dn_field String dn_token = ""; // se dn_fields[] non è null e contiene almeno un elemento if (dn_fields != null && dn_fields.length > 0) { - 181 - APPENDICE - CODICE SORGENTE // Ciclo sugli elementi dell'array dn_fields[] for (int i = 0; i < dn_fields.length; ++i) { // Leggo ciascuna stringa senza spazi a destra e sinistra dn_field = dn_fields[i].trim(); // Estraggo il Nome del Campo dn_token = dn_field.substring(0, dn_field.indexOf("=")).trim(); // Controllo che il Nome del Campo si lo stesso cercato if (dn_token.compareToIgnoreCase(fieldDN) == 0) { // Estraggo il Valore del Campo dn_field_value = dn_field.substring(dn_field.indexOf("=")+1); // Esco dal ciclo break; } } } // Ritorno il Valore del Campo return dn_field_value; } /** * Estrae il campo CountryCode(C) dal DistinguishName * * @param certDN DistinguishName * * @return CountryCode */ public static String extractCountryCode(java.security.Principal certDN) { // Estraggo il campo CountryCode return extractDNField(certDN, "C"); } /** * Estrae il campo CommonName(CN) dal DistinguishName * * @param certDN DistinguishName * * @return CommonName */ public static String extractCommonName(java.security.Principal certDN) { // Estraggo il campo CommonName return extractDNField(certDN, "CN"); } - 182 - APPENDICE - CODICE SORGENTE /** * Estrae il campo Email(E) dal DistinguishName * * @param certDN DistinguishName * * @return Email */ public static String extractEmail(java.security.Principal certDN) { // Estraggo il campo Email return extractDNField(certDN, "EMAILADDRESS"); } /** * Estrae il campo Organization(O) dal DistinguishName * * @param certDN DistinguishName * * @return Organization */ public static String extractOrganization(java.security.Principal certDN) { // Estraggo il campo Organization return extractDNField(certDN, "O"); } /** * Estrae il campo OrganizationUnit(OU) dal DistinguishName * * @param certDN DistinguishName * * @return OrganizationUnit */ public static String extractOrganizationUnit(java.security.Principal certDN) { // Estraggo il campo OrganizationUnit return extractDNField(certDN, "OU"); } /** * Un digest cifrato è quello in cui si usa * una chiave segreta per creare il digest. * Si possono usare chiavi diverse per creare * differenti digest per lo stesso buffer di byte. * * @param algorithm algoritmo da usare per il digest * @param buffer buffer di byte * @param key chiave segreta per la cifratura - 183 - APPENDICE - CODICE SORGENTE * * @return digest */ public static byte[] getKeyedDigest(String algorithm, byte[] buffer, byte[] key) { try { java.security.MessageDigest msgdg = java.security.MessageDigest.getInstance(algorithm); msgdg.update(buffer); return msgdg.digest(key); } catch (java.security.NoSuchAlgorithmException e) { } return null; } /** * Un digest cifrato è quello in cui si usa * una chiave segreta per creare il digest. * Si possono usare chiavi diverse per creare * differenti digest per lo stesso file. * * @param algorithm algoritmo da usare per il digest * @param path nome del file * @param key chiave segreta per la cifratura * * @return digest */ public static byte[] getKeyedDigest(String algorithm, String path, byte[] key) { java.io.FileInputStream in; try { in = new java.io.FileInputStream(path); int bytes = in.available(); byte[] buf = new byte[bytes]; in.read(buf, 0, buf.length); in.close(); return getKeyedDigest(algorithm, buf, key); } catch (Exception ex) { } return null; } /** * Crea il digest a partire da un buffer. * * @param algorithm algoritmo da usare per il digest * @param buffer buffer di byte * - 184 - APPENDICE - CODICE SORGENTE * @return digest */ public static byte[] getDigest(String algorithm, byte[] buffer) { try { java.security.MessageDigest msgdg = java.security.MessageDigest.getInstance(algorithm); msgdg.update(buffer); return msgdg.digest(); } catch (java.security.NoSuchAlgorithmException e) { } return null; } /** * Crea il digest a partire da un file. * * @param algorithm algoritmo da usare per il digest * @param path nome del file * * @return digest */ public static byte[] getDigest(String algorithm, String path) { java.io.FileInputStream in; try { in = new java.io.FileInputStream(path); int bytes = in.available(); byte[] buf = new byte[bytes]; in.read(buf, 0, buf.length); in.close(); return getDigest(algorithm, buf); } catch (Exception ex) { } return null; } } A.1.4 Client.java package it.jtelemed.common; import javax.servlet.http.*; public class Client { - 185 - APPENDICE - CODICE SORGENTE public public public public public String user_name = "-"; String user_kind = "-"; int user_id = -1; String user_labwslink = ""; String user_archivepath = ""; /** * Costruttore pubblico che inizializza le proprietà * */ public Client() { user_id = -1; user_name = "-"; user_kind = "-"; user_labwslink = ""; user_archivepath = ""; } /** * Metodo statico per la creazione di una istanza della * classe Client a partire da un oggetto di sessione HTTP * * @param session oggetto di sessione HTTP * * @return istanza della classe Client */ public static Client create(HttpSession session) { Client cli = new Client(); cli.user_id = new Integer((String)session.getAttribute("USER_ID")).intValue() ; cli.user_name = (String)session.getAttribute("USER_NAME"); cli.user_kind = (String)session.getAttribute("USER_KIND"); cli.user_labwslink = (String)session.getAttribute("USER_LABWSLINK"); cli.user_archivepath = (String)session.getAttribute("USER_ARCHIVEPATH"); return cli; } /** * Metodo * Client * * @param * @param statico per il salvataggio di una classe all'interno di un oggetto di sessione HTTP cli istanza della classe Client session oggetto di sessione HTTP - 186 - APPENDICE - CODICE SORGENTE * */ public static void save(Client cli, HttpSession session) { session.setAttribute("USER_ID",new Integer(cli.user_id).toString()); session.setAttribute("USER_NAME",cli.user_name); session.setAttribute("USER_KIND",cli.user_kind); session.setAttribute("USER_LABWSLINK",cli.user_labwsli nk); session.setAttribute("USER_ARCHIVEPATH",cli.user_archi vepath); } } A.1.5 DateTime.java package it.jtelemed.common; import it.jtelemed.common.StringUtil; public class DateTime { /** * Ritorna la data corrente * * @return restituisce la stringa contenente la data corrente nel formato 'YYYY-MM-DD' */ public static String getCurrentDate() { // Prendo un'istanza della classe Calendar java.util.Calendar c = java.util.Calendar.getInstance(); // Compongo la stringa contenente la data String s = c.get(java.util.Calendar.YEAR) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.MONTH)+1).toString(),2,'0' ,true) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.DATE)).toString(),2,'0',tr ue); // Ritorno la stringa return s; } - 187 - APPENDICE - CODICE SORGENTE /** * Ritorna la data e l'ora corrente * * @return restituisce la stringa contenente la data e l'ora corrente nel formato 'YYYY-MM-DD HH:MM:SS' */ public static String getCurrentDateTime() { // Prendo un'istanza della classe Calendar java.util.Calendar c = java.util.Calendar.getInstance(); // Compongo la stringa contenente la data String s = c.get(java.util.Calendar.YEAR) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.MONTH)+1).toString(),2,'0' ,true) + "-" + StringUtil.padString(new Integer(c.get(java.util.Calendar.DATE)).toString(),2,'0',tr ue) + " " + StringUtil.padString(new Integer(c.get(java.util.Calendar.HOUR_OF_DAY)).toString(),2 ,'0',true) + ":" + StringUtil.padString(new Integer(c.get(java.util.Calendar.MINUTE)).toString(),2,'0', true) + ":" + StringUtil.padString(new Integer(c.get(java.util.Calendar.SECOND)).toString(),2,'0', true); // Ritorno la stringa return s; } /** * Ritorna il timestamp * * @param with_separator specifica se la stringa deve contenere dei separatori * * @return restituisce la stringa contenente la data e l'ora corrente nel formato 'YYYY-MM-DD HH:MM:SS MMMM' */ public static byte[] getTimeStamp(boolean with_separator) { // Prendo un'istanza della classe Calendar java.util.Calendar c = java.util.Calendar.getInstance(); StringBuilder sb = new StringBuilder(); // Compongo la stringa contenente la data sb.append(c.get(java.util.Calendar.YEAR)); - 188 - APPENDICE - CODICE SORGENTE if (with_separator) sb.append("-"); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.MONTH)+1).toString(),2,'0' ,true)); if (with_separator) sb.append("-"); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.DATE)).toString(),2,'0',tr ue)); if (with_separator) sb.append(" "); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.HOUR)).toString(),2,'0',tr ue)); if (with_separator) sb.append(":"); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.MINUTE)).toString(),2,'0', true)); if (with_separator) sb.append(":"); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.SECOND)).toString(),2,'0', true)); if (with_separator) sb.append(" "); sb.append(StringUtil.padString(new Integer(c.get(java.util.Calendar.MILLISECOND)).toString(),2 ,'4',true)); // Ritorno la stringa return sb.toString().getBytes(); } } A.1.6 FileUtil.java package it.jtelemed.common; public class FileUtil { /** * Ritorna il nome del file senza il percorso * * @param path percorso completo del file * * @return nome del file senza il percorso */ public static synchronized String extractFileName(String path) { String s = ""; - 189 - APPENDICE - CODICE SORGENTE // Spezzo la stringa a partire dall'ultimo \ if (path.lastIndexOf("\\") > -1) s = path.substring(path.lastIndexOf("\\")+1); // Spezzo la stringa a partire dall'ultimo / else if (path.lastIndexOf("/") > -1) s = path.substring(path.lastIndexOf("/")+1); else s = path; // Ritorno la stringa return s; } /** * Ritorna l'estensione del file * * @param path percorso completo del file * * @return estensione del file */ public static synchronized String extractFileExt(String path) { String f = extractFileName(path); String s = ""; // Spezzo la stringa a partire dall'ultimo . if (f.lastIndexOf(".") > -1) s = f.substring(f.lastIndexOf(".")+1); return s; } /** * Ritorna il nome del percorso con lo slash finale * * @param path percorso * * @return nome del percorso con lo slash finale */ public static synchronized String parsePathName(String path) { String s = ""; // Controllo che il percorso contenga \ if (path.lastIndexOf("\\") > -1) { // Controllo che il percorso non finisca con \ if (!path.endsWith("\\")) s = path + "\\"; else s = path; } // Controllo che il percorso contenga / - 190 - APPENDICE - CODICE SORGENTE else if (path.lastIndexOf("/") > -1) { // Controllo che il percorso non finisca con / if (!path.endsWith("/")) s = path + "/"; else s = path; } else s = path + "/"; // Ritorno la stringa return s; } /** * Estrae un buffer contenente il file allegato * * @param att allegato al messaggio SOAP * * @return ritorna un buffer che contiene i byte letti */ public static byte[] readFileBuffer(javax.xml.soap.AttachmentPart att) { byte[] buffer = null; try { javax.activation.DataHandler dh = att.getDataHandler(); if (dh != null) { buffer = readFileBuffer(dh.getInputStream()); } } catch (Exception ex) { buffer = null; } return buffer; } /** * Estrae un buffer contenente il file allegato * * @param path nome del file * * @return ritorna un buffer che contiene i byte letti */ public static byte[] readFileBuffer(String path) { byte[] buffer = null; try { - 191 - APPENDICE - CODICE SORGENTE buffer = readFileBuffer(new java.io.FileInputStream(path)); } catch (Exception ex) { buffer = null; } return buffer; } /** * Estrae un buffer contenente il file allegato * * @param in file input strema * * @return ritorna un buffer che contiene i byte letti */ public static byte[] readFileBuffer(java.io.InputStream in) { byte[] buffer = null; int bytes = 0; try { bytes = in.available(); if (bytes > 0) { buffer = new byte[bytes]; in.read(buffer, 0, buffer.length); } } catch (Exception ex) { bytes = -1; } return buffer; } /** * Scrive il buffer nel file * * @param path nome del file da salvare * @param buf byte da scrivere * * @return true se il file è stato salvato */ public static synchronized boolean writeFileBuffer(String path, byte[] buf) { boolean saved = false; try { if (buf != null) { int bytes = buf.length; if (bytes > 0) { - 192 - APPENDICE - CODICE SORGENTE java.io.FileOutputStream out = new java.io.FileOutputStream(path); out.write(buf, 0, buf.length); out.flush(); out.close(); saved = true; } } } catch (Exception ex) { saved = false; } return saved; } /** * Scrive il log nel file * * @param path nome del file da salvare * @param req richiesta da scrivere * * @return true se il file è stato salvato */ public static synchronized boolean logRequest(String path, String req) { boolean saved = false; String row = DateTime.getCurrentDateTime() + "\t" + req + "\r"; try { java.io.FileWriter out = new java.io.FileWriter(path, true); out.write(row); out.flush(); out.close(); saved = true; } catch (Exception ex) { saved = false; } return saved; } /** * Crea un file firmando un buffer di byte * * @param signed_name nome del file digest firmato e serializzato * @param uncript_buffer byte da firmare * - 193 - APPENDICE - CODICE SORGENTE * @return vero se il file firmato è stato creato */ public static synchronized boolean createSignedFile(String signed_name, byte[] uncript_buffer) { try { // Creazione del digest byte[]dig_buf = Certificate.getDigest("SHA1", uncript_buffer); // Operazione di firma del digest e serializzazione it.jtelemed.smartcard.Firma firma = new it.jtelemed.smartcard.Firma(); return firma.cifra(signed_name, dig_buf); } catch (Exception ex) { return false; } } /** * Verifica la firma del file * * @param report_name nome del file da verificare * @param digest_name nome del digest con cui verificare * * @return vero se la firma del file è verificata */ public static synchronized boolean verifySignedFile(String report_name, String digest_name) { try { // Operazione di firma del digest e serializzazione it.jtelemed.smartcard.Firma firma = new it.jtelemed.smartcard.Firma(); byte[] file_buff = FileUtil.readFileBuffer(report_name); byte[] digest_buff = Certificate.getDigest("SHA-1", file_buff); return firma.decifra(digest_name, digest_buff); } catch (Exception ex) { return false; } } - 194 - APPENDICE - CODICE SORGENTE /** * Ritorna il nome di un file di evento * * @param prefix Prefisso del nome del file * @param suffix Suffisso del nome del file * @param evt_id Identificativo dell'evento * * @return (prefix)-(evt_id)(12).(suffix) */ public static synchronized String getEventFileName(String prefix, String suffix, int evt_id) { return prefix + "-" + StringUtil.padString(Integer.toString(evt_id),12,'0',true) + "." + suffix; } /** * Ritorna il nome di un file di referto * * @param prefix Prefisso del nome del file * @param suffix Suffisso del nome del file * @param evt_id Identificativo dell'evento * @param rep_ind Indice del referto * * @return (prefix)-(evt_id)(12)-(rep_ind)(3).(suffix) */ public static synchronized String getReportFileName(String prefix, String suffix, int evt_id, int rep_ind) { return prefix + "-" + StringUtil.padString(Integer.toString(evt_id),12,'0',true) + "-" + StringUtil.padString(new Integer(rep_ind).toString(),3,'0',true) + "." + suffix; } } A.1.7 ServletUtil.java package it.jtelemed.common; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; - 195 - APPENDICE - CODICE SORGENTE import javax.servlet.http.HttpServletResponse; import org.apache.axis.MessageContext; import org.apache.axis.transport.http.HTTPConstants; public final class ServletUtil { /** * Ritorna l'oggetto Servlet * * @return oggetto Servlet */ public static HttpServlet getHttpServlet() { try { MessageContext mContext = MessageContext.getCurrentContext(); return (HttpServlet) mContext.getProperty(HTTPConstants.MC_HTTP_SERVLET); } catch (Exception ex) { return null; } } /** * Ritorna l'oggetto Request * * @return oggetto Request */ public static HttpServletRequest getHttpServletRequest() { try { MessageContext mContext = MessageContext.getCurrentContext(); return (HttpServletRequest) mContext.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST); } catch (Exception ex) { return null; } } /** * Ritorna l'oggetto Response * * @return oggetto Response */ public static HttpServletResponse getHttpServletResponse() { try { - 196 - APPENDICE - CODICE SORGENTE MessageContext mContext = MessageContext.getCurrentContext(); return (HttpServletResponse) mContext.getProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE) ; } catch (Exception ex) { return null; } } /** * Ritorna il messaggio SOAP di richiesta * * @return messaggio SOAP */ public static javax.xml.soap.SOAPMessage getSOAPRequestMessage() { try { MessageContext mContext = MessageContext.getCurrentContext(); javax.xml.soap.SOAPMessage mSoapMsg = mContext.getRequestMessage(); return mSoapMsg; } catch (Exception ex) { return null; } } /** * Ritorna il messaggio SOAP di risposta * * @return messaggio SOAP */ public static javax.xml.soap.SOAPMessage getSOAPResponseMessage() { try { MessageContext mContext = MessageContext.getCurrentContext(); javax.xml.soap.SOAPMessage mSoapMsg = mContext.getResponseMessage(); return mSoapMsg; } catch (Exception ex) { return null; } } /** - 197 - APPENDICE - CODICE SORGENTE * Ritorna il parametro di inizializzazione * * @param name nome del parametro * * @return valore del parametro */ public static String getInitParameter(String name) { try { HttpServlet serv = getHttpServlet(); return(serv.getServletContext().getInitParameter(name) ); } catch (Exception ex) { return ""; } } /** * Ritorna il parametro di inizializzazione <i>name</i> della <i>servlet</i> * * @param servlet servlet di cui leggere i parametri * @param name nome del parametro * * @return valore del parametro */ public static String getInitParameter(HttpServlet servlet, String name) { if (servlet == null) throw new NullPointerException("Parameter Servlet cannot be null."); if (name == null) throw new NullPointerException("Parameter Name cannot be null."); if (name.length() < 1) throw new IllegalArgumentException("Parameter Name cannot be empty."); try { return(servlet.getServletContext().getInitParameter(na me)); } catch (Exception ex) { return ""; } } } - 198 - APPENDICE - CODICE SORGENTE A.1.8 StringUtil.java package it.jtelemed.common; public class StringUtil { /** * Ritorna la stringa racchiusa tra apici * pronta per essere inserita in una query * * @param s */ public static synchronized String quoteString(String s) { return("'" + s.replaceAll("'","''") + "'"); } /** * pad a string S with a size of N with char C * on the left (True) or on the right(flase) * * @param s stringa * @param n dimensione * @param c carattere di pad * @param paddingLeft true a sinistra, false a destra */ public static synchronized String padString(String s, int n, char c, boolean paddingLeft ) { StringBuffer str = new StringBuffer(s); int strLength = str.length(); if (n > 0 && n > strLength) { for (int i = 0; i <= n; i ++) { if (paddingLeft) { if (i < n - strLength) str.insert(0, c); } else { if (i > strLength) str.append(c); } } } return str.toString(); } /** * Ritorna la stringa con le iniziali in maiuscolo * * @param s - 199 - APPENDICE - CODICE SORGENTE */ public static synchronized String capitalizeString(String s) { String[] tks = s.split(" "); String cs = ""; if (tks != null && tks.length > 0) { for (int i = 0; i < tks.length; ++i) { if (tks[i] != null && tks[i].length() > 1) cs += tks[i].substring(0,1).toUpperCase() + tks[i].substring(1); else cs += tks[i].substring(0,1).toUpperCase(); } } // Ritorno la stringa return cs; } } - 200 - APPENDICE - CODICE SORGENTE A.2 WEB SERVICE REPOSITORY A.2.1 WSRepository.java package it.jtelemed.services; import it.jtelemed.common.*; public class WSRepository { private Application wsApp = null; public WSRepository() { wsApp = new Application(); // Lettura dei parametri di inizializzazione wsApp.setUrl(ServletUtil.getInitParameter("url")); wsApp.setUser(ServletUtil.getInitParameter("user")); wsApp.setPassword(ServletUtil.getInitParameter("passwo rd")); wsApp.setDriverClass(ServletUtil.getInitParameter("dri verClass")); wsApp.setWelcomeMsg(ServletUtil.getInitParameter("welc omeMsg")); wsApp.setReportPath(ServletUtil.getInitParameter("repo rtPath")); } /** * Funzione di test del webservice * * @param value stringa che deve essere restituita * * @return stringa di benvenuto + stringa parametro */ public String echo(String value){ return(wsApp.getWelcomeMsg() + value); } /** - 201 - APPENDICE - CODICE SORGENTE * Ritorna le informazioni dell'utente con il CommonName specificato * * @param utn_cn CommonName dell'utente * * @return elenco delle informazioni dell'utente */ public String userinfo(String utn_cn) { String info = ""; info = wsApp.getUserInfo(utn_cn); return info; } /** * Ritorna l'elenco degli eventi * * @param utn_id Identificativo dell'utente * * @return elenco degli eventi in base alle autorizzazioni dell'utente */ public String listevents(int utn_id) { String list = ""; if (utn_id > 0) list = wsApp.selectEvents(utn_id); return list; } /** * Apertura dell'evento * * @param utn_id Identificativo dell'utente * @param evt_codice Codice dell'esame da Tariffario * @param evt_esterno evt_esterno * @param evt_token evt_token * @param evt_wslink evt_wslink * @param evt_richiedente evt_richiedente * @param evt_impegnativa evt_impegnativa * @param evt_note evt_note * * @return evt_id Identificativo dell'evento */ public int openevent(int utn_id, String evt_codice, boolean evt_esterno, String evt_token, String evt_wslink, String evt_richiedente, String evt_impegnativa, String evt_note) { - 202 - APPENDICE - CODICE SORGENTE int evt_id = -1; evt_id = wsApp.openEvent(utn_id, evt_codice, evt_esterno, evt_token, evt_wslink, evt_richiedente, evt_impegnativa, evt_note); return evt_id; } /** * Chiude un evento esistente * * @param evt_id Identificativo dell'evento * * @return vero se la chiusura è avvenuta con successo */ public boolean closeevent(int evt_id) { boolean closed = true; if (evt_id > 0) closed = wsApp.closeEvent(evt_id); return closed; } /** * Ritorna l'elenco dei referti e li allega * * @param evt_id Identificativo dell'evento * * @return elenco dei referti */ public String getreports(int evt_id) { String list = ""; if (evt_id > 0) list = wsApp.getReportFiles(evt_id); return list; } /** * Carica nel repository un nuovo referto per l'evento * * @param evt_id Identificativo dell'evento * @param utn_id Identificativo dell'utente * @param evt_content Contenuto del referto * * @return vero se l'upload è stato effettuato correttamente - 203 - APPENDICE - CODICE SORGENTE */ public boolean uploadreport(int evt_id, int utn_id, String evt_content) { boolean saved = false; if (evt_id > 0) saved = wsApp.uploadReport(evt_id, utn_id, evt_content); if (saved) wsApp.updateEvent(evt_id); return saved; } /** * Invalida un referto di un evento esistente * * @param evt_id Identificativo dell'evento * @param ref_ind Indice del referto * * @return vero se il referto è stato invalidato */ public boolean invalidatereport(int evt_id, int ref_ind) { boolean invalid = true; if (evt_id > 0 && ref_ind >0) invalid = wsApp.invalidateReport(evt_id, ref_ind); return invalid; } } - 204 - APPENDICE - CODICE SORGENTE A.3 WEB SERVICE LABORATORY A.3.1 WSLaboratory.java package it.jtelemed.services; import it.jtelemed.common.*; public class WSLaboratory { private Application wsApp = null; public WSLaboratory() { wsApp = new Application(); // Lettura dei parametri di inizializzazione wsApp.setWelcomeMsg(ServletUtil.getInitParameter("welc omeMsg")); wsApp.setArchivePath(ServletUtil.getInitParameter("arc hivePath")); } /** * Funzione di test del webservice * * @param value stringa che deve essere restituita * * @return stringa di benvenuto + stringa parametro */ public String echo(String value){ return("Web Service Laboratoy: " + value); } /** * Allega il file di risorsa dell'evento * * @param file nome della risorsa * * @return restituisce vero se è andato tutto bene */ public boolean getevent(String file) { boolean ret = false; - 205 - APPENDICE - CODICE SORGENTE FileUtil.logRequest("C:\\wslab.log", "getevent?file=" + file); if (file != null && file.length() > 0) ret = wsApp.getEventFile(file); FileUtil.logRequest("C:\\wslab.log", "getEventFile:" + new Boolean(ret).toString()); return ret; } } - 206 - BIBLIOGRAFIA BIBLIOGRAFIA Riferimenti telemedicina Molino G., La cartella clinica va on line, Il sole 24 ore Sanità – Management. Dicembre 2002 Nonis M., Braga M., Guzzanti E. Cartella, Clinica e qualità dell’assistenza. Passato, presente e futuro, Il Pensiero Scientifico Editore. Roma 1998. Salvadori P., Informatizzazione e Distretto Socio Sanitario, Panorama della Sanità, anno XV N° 39, ottobre 2002. Hakansson S., Gavelin C., What do we really know about the cost–effectiveness of telemedicine?, Journal of Telemedicine and Telcare 2000;6(S1):S133 M.Ciulla, R.Paliotti, F.Magrini, Archivio ecocardiografico multimediale, Cardiologia 1998: 43 n 2: 195-2006 M. Ciulla, R. Paliotti, M.V. Barelli, P.Valentini, F. Magrini Internet: cosa offre la rete informatica al medico XCVIII Congresso Naz Soc It di Medicina Interna, 1997 Roma - 207 - BIBLIOGRAFIA Riferimenti tecnologia Feghhi J., Digital Certificates – Applied Internet Security, Addison-Wesley 2000 Jayson Falkner e Kevin Jones, Servlets and JavaServer Pages™: The J2EE™ Technology Web Tier, AddisonWesley 2003 James Turner, MySQL™ and JSP™ Web Applications: Data-Driven Programming Using Tomcat and MySQL, Sams 2002 James Goodwill, Apache Axis Live, SourceBeat LLC 2004 James R. Groff,Paul N. Weinberg, SQL - The Complete Reference, McGraw-Hill 2003 Elliotte Rusty Harold, Java Network Programming, Second Edition, O’Reilly 2000 Scott Oaks, Java Security, Second Edition, O’Reilly 2001 Jason Hunter, Java Servlet Programming, Second Edition, O’Reilly 2001 - 208 - BIBLIOGRAFIA John Viega, Matt Messier, Pravir Chandra, Network Security with OpenSSL, First Edition, O’Reilly 2002 Steve Graham, Simeon Simeonov, Toufic Boubez, Doug Davis, Glen Daniels, Yuichi Nakamura, Ryo Neyama, Building Web Services with Java™: Making Sense of XML, SOAP, WSDL, and UDDI, Sams Publishing 2001 [WSCA10] Heather Kreger, 2001, IBM, Web Services Conceptual Architecture 1.0 [SOAP11] W3C, Simple Object Access Protocol (SOAP) 1.1 [WSDL11] W3C, Web Services Description Language (WSDL) 1.1 [UDDI-TWP] Uddi.org, UDDI Technical White Paper Riferimenti web Articoli su http://www.latoserver.it/ Struttura di una applicazione web - Luca Balzerani 18/04/2002 L'ambiente di Tomcat - Massimo Vassalli 26/02/2002 Installazione di Apache Tomcat - Giuseppe Capodieci 13/03/2001 - 209 - BIBLIOGRAFIA Java Server Pages, un'introduzione - Andrea De Paoli 10/07/2002 Java lato server: le Servlet - Giuseppe Capodieci 24/11/2001 Articoli su http://www.itportal.it/ SOAP: l'ultima frontiera per lo scambio dei dati Francesco Zumpano 02-05-2001 Articoli su http://www.weekit.it/ Articoli su http://programmazione.html.it/ Software impiegato 1. Java 2 Platform Standard Edition 5.0 Development Kit (JDK) Update 5 jdk-1_5_0_05-windows-i586-p.exe from http://java.sun.com/j2se/1.5.0/download.jsp 2. Java 2 Platform Standard Edition 5.0 Documentation jdk-1_5_0-doc.zip from http://java.sun.com/j2se/1.5.0/download.jsp 3. Apache Tomcat 5.5.9 Binary jakarta-tomcat-5.5.9.exe from http://jakarta.apache.org/site/downloads/ 4. Lomboz 3.1 Release Candidate 2 lomboz-eclipse-emfgef-jem-3.1RC2.zip from http://forge.objectweb.org/projects/lomboz 5. MySQL 4.1.14 Database Server mysql-4.1.14win32.zip from http://dev.mysql.com/downloads/mysql/4.1.html - 210 - BIBLIOGRAFIA 6. MySQL Java Connector 3.0.17 mysql-connector-java3.0.17-ga.zip from http://dev.mysql.com/downloads/connector/j/3.0.html 7. Exadel Studio 3.0.4 ExadelStudio-3.0.4.exe from http://www.exadel.com/products_exadelstudio.htm 8. OpenSSL 0.9.8a Toolkit openssl-0.9.8a.tar.gz from http://www.openssl.org/source/ - 211 -