UNIVERSITÀ POLITECNICA DELLE MARCHE FACOLTÀ DI INGEGNERIA Corso di Laurea in Ingegneria Informatica e dell’Automazione Telerefertazione medica su TCP/IP: Progetto e Realizzazione degli opportuni Web-Service Tesi di laurea di: D’ALBERTO STEFANO Relatore: PROF. ING. ALDO FRANCO DRAGONI Correlatori PROF. PAOLO PULITI PROF. GUIDO TASCINI Anno Accademico 2006/2007 Capitolo 1. Struttura del progetto 1.1 1.2 1.3 1.4 Scopo e contenuto del documento…………………………........ pag. 3 Descrizione del progetto applicativo…………………………… pag. 3 Modello organizzativo………………………………………..... pag. 5 Glossario dei termini…………………………………………… pag. 7 Capitolo 2. Il Web service 2.1 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.3 2.4 Introduzione al Web service.................………………………... pag. 8 Cosa sono i Web services e come funzionano..............………... pag. 10 XML Schema…………………………………………………... pag. 11 UDDI…………………………………………………………… pag. 13 WSDL………………………………………………………….. pag. 13 SOAP…………………………………………………………… pag. 15 Perché creare un Web Service...............………………………... pag. 17 Apache Axis...............……………………………………...…... pag. 18 Capitolo 3. Preparazione dell’ambiente di sviluppo 3.1 3.3 Lato Server……………………………………………………... pag. 21 Fase di deploy………………………………………………….. pag. 36 Capitolo 4. La comunicazione Client-Server 4.1 4.2 Servizi offerti lato server……………………………………….. pag. 41 Axis Attachment……………...………………………………… pag. 57 Capitolo 5. Struttura del database 5.1 5.2 5.3 Specifica delle operazioni……………………………………… pag. 60 Specifiche di progetto………………………………………….. pag. 61 Progettazione concettuale della base di dati……………………. pag. 68 Un primo schema scheletro…………………………………….. pag. 68 1 5.3.1 5.3.2 5.4 5.5 Sviluppo delle componenti dello scheletro (inside-out)………... pag. 68 Traduzione verso il modello relazionale……………………….. pag. 75 Codifica SQL…………………………………………………... pag. 78 Capitolo 6. Configurazione Server di sviluppo 6.1 6.2 6.3 6.4 6.5 Ambiente di sviluppo.………………………………………... pag. 82 Installazione Java sul Server ……….………………………….. pag. 83 Installazione TOMCAT su Server …………………….………. pag. 83 Installazione Oracle Database 10g XE…………...…….………. pag. 85 Installazione e configurazione di Apache …...………...………. pag. 92 Capitolo 7. Problematiche e miglioramenti 7.1 7.2 7.3 Il server Linux.………………………….……………………... pag. 94 Interoperabilità ed evoluzioni future…………………………… pag. 97 Possibili sviluppi futuri………...…………………….………. pag. 98 Capitolo 8. Pubblicazione 8.1 8.2 Il firewall di Linux……………………………………………... pag. 101 Il comando iptables…………...…….………………………….. pag. 102 2 1. Struttura del progetto 1.1 Scopo e contenuto del documento Scopo del presente documento è definire le specifiche per l’impostazione lato client e lato server sia a livello concettuale che fisico dell’applicativo JTELEMED. 1.2 Descrizione del Progetto Applicativo Per Telemedicina si intende l’erogazione di servizi sanitari, laddove la distanza rappresenti un fattore critico da parte di professionisti nell’assistenza sanitaria che utilizzino tecnologie dell’informazione e della comunicazione per lo scambio di informazioni rilevanti, per la diagnosi, il trattamento e la prev.enzione delle patologie e per l’educazione continuativa degli operatori sanitari, nell’interesse del miglioramento della salute e delle comunità assistite. Figura 1 – Telemedicina La Telemedicina utilizza le tecnologie della telecomunicazione per erogare assistenza 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. 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; 3 Capitolo 1 • Struttura del progetto 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. Nell’ottica di capillarizzare l’offerta sanitaria, può essere importante pensare ad una struttura informatica che renda indipendenti le due fasi tipiche della diagnostica: • l’esecuzione dell’esame • la sua refertazione. Mentre è possibile pensare di localizzare alcuni macchinari (di basso costo) anche nei poliambulatorii più piccoli o distanti, può risultare sconveniente portare in questi poliambulatorii personale di alta specializzazione. Il termine Telemedicina si presta a svariate definizioni, non sempre univoche in letteratura, che spesso focalizzano l’attenzione solo su alcuni aspetti della materia. Si tratta sostanzialmente della trasmissione in tempo reale di informazioni a carattere scientifico tra medico e cittadino o tra addetti ai lavori, attraverso sistemi di comunicazione di tipo telematico/informatico. La definizione più esaustiva del termine è senz’altro quella concordata a livello CEE da una Commissione di esperti, che ha redatto un documento sulle prospettive di sviluppo della telemedicina in Europa (Advanced Informatics in Medicine - AIM 1990) con l’obiettivo di migliorare la qualità dei servizi sanitari, facilitare la formazione professionale di medici e infermieri ed ottimizzare il trasferimento qualificato di dati ed esperienze tra i vari Paesi europei. Secondo la Commissione Europea, organizzatrice tra l’altro dell’EHTO (European Health Telematics Observatory – Osservatorio delle applicazioni mediche della telematica), la telemedicina è "l’integrazione, monitoraggio e gestione dei pazienti, nonché l’educazione dei pazienti e del personale, usando sistemi che consentano un pronto accesso alla consulenza di esperti ed alle informazioni del paziente, indipendentemente da dove il paziente o le informazioni risiedano". 4 Capitolo 1 Struttura del progetto 1.3 Modello organizzativo Figura 2 – Modello Organizzativo L’applicativo JTELEMED consta di tre entità: • RICHIEDENTE • REFERTANTE (o EROGANTE) • REPOSITORY Il RICHIEDENTE è caratterizzato dall’insieme delle 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. Una prerogativa importante di tutto il nostro applicativo è il mantenimento della privacy. Per questo motivo i richiedenti non saranno mai individuati attraverso dati anagrafici personali ma attraverso un identificativo univoco. Il REFERTANTE è, invece, l’insieme di coloro che forniscono il servizio di refertazione, una clinica specializzata, un semplice medico o una equipe di medici accreditati. Il cuore del sistema è rappresentato dal REPOSITORY CENTRALE, che altro non è che un server relazionale, cioè un server che gestisce un DataBase. 5 Capitolo 1 Struttura del progetto L’effettuazione di un esame presso un laboratorio 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. E’ composto da una serie di 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, il dottore richiedente ed il codice dell’impegnativa, lo stato della fase di refertazione, e, cosa molto importante il link che consente di scaricare il dato. Ogni evento quindi viene immagazzinato all’interno di un apposito raccoglitore, definito appunto repository. Qualsiasi esame che può essere memorizzato in forma digitale può essere associato ad un evento. Chiarito il funzionamento dell’applicativo, non rimane altro che definire un meccanismo di comunicazione tra il Repository e i suoi utilizzatori. Come già detto in precedenza il repository centrale non è altro che un server relazionale ovvero un server che gestisce un database; in realtà il suo funzionamento è ben più ampio in quanto, oltre ad interfacciarsi con la base di dati, esso fornisce servizi accessibili con un semplicissimo browser sia lato richiedente che lato refertante: esso realizza cioè un’architettura web service. Nel prossimo capitolo vedremo nel dettaglio la sua realizzazione. 6 Capitolo 1 Struttura del progetto 1.4 Glossario dei termini Termine JTelemed Telemedicina Esame clinico Diagnosi Cura Richiedente Refertante Refertazione Laboratori Evento Repository Descrizione E’ l’applicativo che si vuole realizzare al fine di fornire un servizio di telemedicina a distanza Uso di strumenti telematici per effettuare esami clinici a distanza Indagine specialistica a scopo diagnostico di un organismo vivente o di un organo. Individuazione del quadro morboso di un paziente in base alla valutazione dei sintomi, all’anamnesi e alle analisi strumentali e di laboratorio Insieme dei rimedi usati per guarire da una malattia Strutture accreditate che producono i dati digitali di un esame clinico e che si interfacciano nel nostro sistema. Colui che fornisce il servizio di refertazione Sinonimi Applicativo Collegamenti Telemedicina Sistema telematico JTelemed di assistenza medica Esame, Analisi, Telemedicina Ricerca, Indagine Valutazione, Opinione, Accertamento Refertante, Refertazione Terapia, Trattamento Refertante, Refertazione Utente, Client di Esame clinico laboratorio, Utente richiedente Clinica, Laboratorio, Laboratorio Medico, Utente Erogante, Utente Refertante Il risultato fornito dal Diagnosi Diagnosi, Cura refertante derivante da una diagnosi Locali dotati di apposite Clinica Refertante apparecchiature per ricerche cliniche Effettuazione di un esame Esito, Risultato Richiedente, presso un laboratorio e Refertante, conseguente archiviazione Repository del dato in forma digitale Server relazionale, ovvero Server Richiedente, un server che gestisce un Refertante, Evento DataBase. 7 2. Il Web service 2.1 Introduzione al Web service Il paradigma del Service Oriented Computing è visto come una rivoluzione nella comunità informatica e i Web Service una sua realizzazione. La possibilità di vedere il Web come un grande sistema informativo in cui sono forniti innumerevoli servizi offre agli utenti finali un potentissimo strumento che va al di là del mero scambio di informazioni che al momento rappresenta il Web. I servizi web, meglio noti come web services, sono diventanti uno degli argomenti più attuali nel panorama dello sviluppo in ambiente Internet. Posti al centro delle più recenti strategie di aziende del calibro di IBM, Microsoft e Sun, vengono spesso descritti come una vera e propria rivoluzione nel mondo del web ed in particolare per tutto quanto attiene allo sviluppo di applicazioni distribuite ed all'integrazione di applicazioni. Secondo la definizione data dal World Wide Web Consortium (W3C) un Web Service (servizio web) è un sistema software progettato per supportare l'interoperabilità tra diversi elaboratori su di una medesima rete; caratteristica fondamentale di un Web Service è quella di offrire un'interfaccia software (descritta in un formato automaticamente elaborabile quale, ad esempio, il Web Services Description Language) utilizzando la quale altri sistemi possono interagire con il Web Service stesso attivando le operazioni descritte nell'interfaccia tramite appositi "messaggi" inclusi in una "busta" SOAP: tali messaggi sono, solitamente, trasportati tramite il protocollo HTTP e formattati secondo lo standard XML. Focalizzando l’attenzione sul concetto di servizio è ovvio immaginare, anche alla luce di quanto detto finora, come gli attori in causa siano necessariamente il fornitore e il richiedente. Questo tipo di paradigma è il medesimo che si riscontra nella tipica interazione di tipo client-server. Attraverso la SOA (Service Oriented Architecture) questa interazione viene arricchita con un ulteriore attore detto Service Directory o Service Broker che, come mostrato in figura 1, si inserisce all’interno della comunicazione tra fornitore e fruitore del servizio. 8 Capitolo 2 Il Web Service Figura 3 – Service Oriented Architecture Service Provider Chi realizza e mette a disposizione un servizio. Tramite l’operazione di publish il servizio viene “pubblicizzato”, in quanto le caratteristiche del servizio realizzato vengono memorizzate all’interno di un registry accessibile pubblicamente. Il Service Provider rimane, quindi, in attesa che un utente richieda tale servizio. Service Directory o Service Broker Questo componente si occupa della gestione del registry, permettendo, a chi ha necessità, di ricercare un servizio sulla base delle caratteristiche con le quali è stato definito e memorizzato. Naturalmente, il Service Directory può seguire politiche di controllo degli accessi sulle interrogazioni in modo da limitare la visibilità sui servizi inseriti. Nel presente lavoro il registry, viene considerato parzialmente accessibile. Service Requestor Rappresenta un potenziale utente che richiede un servizio. A tale scopo, tramite la primitiva di find l’utente interagisce con il Service Directory per ottenere il servizio più adatto ai propri obiettivi. Una volta individuato si collega al Service Provider corrispondente (bind) e inizia a fruire del particolare servizio (use). 9 Capitolo 2 Il Web Service Partendo da questa considerazione si può dire che una architettura per e-Service è un’istanza di una SOA dove il mezzo di comunicazione è di tipo elettronico, mentre una architettura per Web Service è un’istanza di una SOA dove il mezzo di comunicazione considerato è il Web. Figura 4 – Pila Protocollare dei Web Service 2.2 Cosa sono i Web services e come funzionano Un Web service è un componente applicativo. Possiamo definirlo come un sistema software in grado di mettersi al servizio di un applicazione comunicando su di una medesima rete tramite il protocollo HTTP. Un Web service consente quindi alle applicazioni che vi si collegano di usufruire delle funzioni che mette a disposizione. Esso comunica tramite protocolli e standard definiti "aperti" e quindi sempre a disposizione degli sviluppatori ed ha una caratteristica molto particolare ed utile al suo scopo: è autocontenuto ed auto-descrittivo, cioè è in grado di farci sapere che funzioni mette a disposizione (senza bisogno di conoscerle a priori) e ci permette inoltre di capire come vanno utilizzate. Il protocollo HTTP si occupa di mettere in comunicazione il servizio web con l'applicazione che intende usufruire delle sue funzioni. Oltre ad HTTP però, i servizi web utilizzano molti altri standard web, tutti basati su XML, tra cui: 10 Capitolo 2 • XML Schema • UDDI (Universal Description, Discovery and Integration) • WSDL (Web Services Description Language) • SOAP (Simple Object Access Protocol) Il Web Service È importante sottolineare che XML può essere utilizzato correttamente tra piattaforme differenti (Linux, Windows, Mac) e differenti linguaggi di programmazione. XML è inoltre in grado di esprimere messaggi e funzioni anche molto complesse e garantisce che tutti i dati scambiati possano essere utilizzati ad entrambi i capi della connessione. Si può quindi dire che i Web service sono basati su XML ed HTTP e che possono essere utilizzati su ogni piattaforma e con ogni tipo di software. 2.2.1 XML Schema Abbiamo detto che un Web service è auto-descrittivo. Con XML Schema cominceremo a capire come fa un servizio web a disporre di questa caratteristica. XML Schema serve per definire qual è la costruzione legale di un documento XML, come fanno per esempio i DTD con le pagine web. Vediamo una lista delle principali funzioni di XML Schema: • Definire gli elementi (tag) che possono apparire in un documento • Definire gli attributi che possono apparire in un elemento • Definire quali elementi devono essere inseriri in altri elementi (child) • Definire il numero degli elementi child • Definire quando un elemento deve essere vuoto o può contenere testo, elementi, oppure entrambi • Definire il tipo per ogni elemento e per gli attributi (intero, stringa, ecc, ma anche tipi personalizzati) • Definire i valori di default o fissi per elementi ed attributi Ecco un esempio di XML Schema in grado di descriverlo: 11 Capitolo 2 Il Web Service <?xml version="1.0"?> <!-- iniziamo lo schema --> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.XXXX.it/Guida_ai_Webservice" xmlns="http://www.XXXX.it/Guida_ai_Webservice" elementFormDefault="qualified"> <!-- definiamo l'elemento libro --> <xs:element name="libro"> <xs:complexType> <xs:all> <!-- definiamo i vari elementi child di libro --> <xs:element name="titolo" type="xs:string" /> <xs:element name="autore" type="xs:string" /> <xs:element name="numeroDiPagine" type="xs:integer" /> </xs:all> </xs:complexType> </xs:element> </xs:schema> Per ora concentriamoci sul primo elemento: schema. Questo è l'elemento che racchiude tutti i tipi di dato che andiamo a descrivere. Il tag <schema> può avere diversi attributi. • xmlns:xs="..." stabilisce che gli elementi che iniziano con 'xs' provengono dal namespace http://www.w3.org/2001/XMLSchema . • targetNamespace="..." definisce a quale namespace appartengono gli elementi definiti con questo schema . • xmlns="..." definisce il namespace di default (quello applicato se non ne escplicitiamo uno) • elementFromDefault="qualified" indica che ogni elemento usato nel documento XML che utilizzerà questo schema dovrà essere qualificato. Una volta aperto il tag <schema> bisogna inserire al suo interno tutti gli elementi che vogliamo definire per il nostro documento. Esistono sostanzialmente due tipi di elementi: • gli elementi semplici non possono contenere altri elementi e avere attributi. Possono contenere solo testo. 12 Capitolo 2 • Il Web Service gli elementi complessi possono contenere testo, altri elementi e attributi in qualsiasi combinazione. 2.2.2 UDDI L'UDDI (acronimo di Universal Description Discovery and Integration) è un registry (ovvero una base dati ordinata ed indicizzata), basato su XML ed indipendente dalla piattaforma hardware, che permette alle aziende la pubblicazione dei propri dati e dei servizi offerti su internet. L ‘UDDI permette quindi la scoperta e l'interrogazione dei servizi offerti sul web, delle aziende che li offrono e della maniera per usufruirne. Una "registrazione" UDDI consiste, infatti, di tre diverse componenti: • Pagine bianche (White Pages): indirizzo, contatti (dell'azienda che offre uno o più servizi) e identificativi; • Pagine gialle (Yellow Pages): categorizzazione dei servizi basata su tassonomie standardizzate; • Pagine verdi (Green Pages): informazioni (tecniche) dei servizi fornite dall'azienda L'UDDI è uno degli standard alla base del funzionamento dei Web Service: è stato progettato per essere interrogato da messaggi in SOAP e per fornire il collegamento ai documenti WSDL che descrivono i vincoli protocollari ed i formati dei messaggi necessari per l'interazione con i Web Service elencati nella propria directory. 2.2.3 WSDL Il WSDL serve a specificare dove si trovano i servizi e le operazioni esposte dal servizio web. Cominciamo subito a vedere come un documento WSDL definisce un Web service. All'interno del documento esistono quattro elementi principali: • <types> • <message> 13 Capitolo 2 Il Web Service • <portType> • <binding> <definitions> <types> <!-- definizione dei tipi di dato utilizzati... --> </types> <message> <!-- definizione di uno dei messaggi impiegati dal web service per comunicare con l'applicazione client -> </message> <!-- naturalmente può esistere più di un elemento message all'interno del documento --> <portType> <!-- definisce una "porta" e le operazioni che possono essere eseguite dal web service. Definisce inoltre i messaggi coinvolti nelle operazioni elencate --> </portType> <binding> <!-- definisce il formato del messaggio ed i dettagli di protocollo per ogni porta --> </binding> </definitions> I types, message e portType sono elementi del messaggio WSDL all’interno di definitions e sono usati per definire le operazioni: • <types> è usato per definire i tipi base necessari allo scambio dell’informazione. • <message> è usato per definire il messaggio spedito e ricevuto ed utilizza i tipi definiti in types. • <portTypes> è usato per definire il funzionamento delle porte allocate dal servizio come i messaggi da utilizzare in input e output. Gli elementi binding e service sono usati per definire il protocollo associato all’operazione: • <binding> è usato per definire il protocollo da utilizzare per comunicare con la porta su cui è allocata l’operazione (HTTP, SOAP,…). • <service> è usato per definire una porta come URL attraverso la quale si trova il servizio. 14 Capitolo 2 Il Web Service • Il WSDL inoltre definisce quattro tipi di operazioni attraverso il tag <operations>: – One-way: è una chiamata asincrona al servizio. – Request-responce: chiamata sincrona al servizio. – Sollicit-responce: invia una risposta dopo un sollecito. – Notification: ricevere una notifica. 2.2.4 SOAP E’ utile sapere che tutte le informazioni che vengono definite da WSDL, è grazie a SOAP (Simple Object Access Protocol) se vengono scambiate tra il Web service è l'applicazione che vi accede. Questo protocollo fornisce una via per comunicare tra applicazioni eseguite su sistemi operativi diversi, con diverse tecnologie e linguaggi di programmazione, tramite HTTP ed XML. Un messaggio SOAP è un documento XML che contiene i seguenti elementi: • Envelope, identifica il documento come un messaggio SOAP; • Un elemento Header opzionale, contenete informazioni specifiche per l'applicazione, che non sarà approfondito in questa sede ma che permette di definire alcuni messaggi, anche con diversi destinatari nel caso il messaggio dovesse attraversare più punti di arrivo; • Body è un elemento indispensabile che contiene le informazioni scambiate dalle richieste/risposte; • Fault è un elemento opzionale che fornisce informazioni riguardo ad eventuali errori manifestati durante la lettura del messaggio. Le regole principali per realizzare un messaggio SOAP sono le seguenti: • Deve essere ovviamente codificato con XML • Deve utilizzare il SOAP Evenelope namespace (http://www.w3.org/2001/12/soapenvelope) • Deve utilizzare il SOAP Encoding namespace (http://www.w3.org/2001/12/soapencoding) 15 Capitolo 2 • Il Web Service Non deve contenere il collegamento ad un DTD e non deve contenere istruzioni per processare XML Lo "scheletro" di un messaggio SOAP: <?xml version="1.0"?> <soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope (http://www.w3.org/2001/12/soap-envelope)" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> <soap:Header> ... </soap:Header> <soap:Body> ... <soap:Fault> ... </soap:Fault> </soap:Body> </soap:Envelope> All'interno dell'elemento Envelope abbiamo definito i namespace soap evelope ed encoding che come abbiamo visto sono richiesti per questo tipo di documento. Se non vengono definiti o si definiscono diversamente le applicazioni coinvolte nella comunicazione potrebbero generare un errore o scartare il messaggio. 16 Capitolo 2 Il Web Service 2.3 Perché creare un Web Service La ragione principale per la creazione e l'utilizzo di Web Service è il "disaccoppiamento" che l'interfaccia standard esposta dal Web Service rende possibile fra il sistema utente ed il Web Service stesso: modifiche ad una o all'altra delle applicazioni possono essere attuate in maniera "trasparente" all'interfaccia tra i due sistemi; tale flessibilità consente la creazione di sistemi software complessi costituiti da componenti svincolati l'uno dall'altro e consente una forte riusabilità di codice ed applicazioni già sviluppate. I Web service hanno inoltre guadagnato consensi visto che, come protocollo di trasporto, possono utilizzare HTTP "over" TCP sulla porta 80; tale porta è, normalmente, una delle poche (se non l'unica) lasciata "aperta" dai sistemi firewall al traffico di entrata ed uscita dall'esterno verso i sistemi aziendali e ciò in quanto su tale porta transita il traffico HTTP dei web browser: ciò consente l'utilizzo dei Web Service senza modifiche sulle configurazioni di sicurezza dell'azienda (un aspetto che se da un lato è positivo solleva preoccupazioni concernenti la sicurezza). Un'ultima ragione che ha favorito l'adozione ed il proliferare dei Web Service è la mancanza, prima dello sviluppo di SOAP, di interfacce realmente funzionali per l'utilizzo di funzionalità distribuite in rete: EDI, RPC, ed altri tipi di API (Application Programming Interface) erano e rimangono meno conosciute e di facile utilizzo che non l'architettura dei Web Service. 17 Capitolo 2 Il Web Service 2.4 Apache Axis Axis è il motore opensource più famoso per la creazione di Web Services in Java. Axis è un progetto dell'Apache Software Fondation e deriva da SOAP4J, un progetto regalato dall'IBM ad Apache. Brevemente, si tratta di un’API di programmazione e deployment di Web services che permette di lavorare ad un livello di astrazione elevato, evitando così di dover maneggiare direttamente l’envelope SOAP. Con Axis è possibile, dunque, implementare Web services e anche sviluppare client di servizi di terzi. Per semplicità, negli esempi che tratteremo viene esposto solo l’aspetto di funzionalità RPC e non quello relativo ai vari approcci di messaging supportati da Axis. Ciò è comunque in linea con la maggior parte della casistica Web services che si basa su meccanismi request/response a dispetto del modello asincrono. Axis 1.4 disponibile all’URL http://ws.apache.org/axis/ è composto da: • una web application che si occupa di gestire l'ambiente di esecuzione dei servizi (routing, istance pooling, serializzazione e deserializzazione 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 serie 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; • 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. 18 Capitolo 2 Il Web Service 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. Le applicazioni client che utilizzano gli stub non necessitano di ambienti di esecuzione particolari ma soltanto della presenza della libreria axis.jar nel proprio classpath; • la comunicazione via JAX-RPC tra processi Java: le API di Axis implementano una versione di JAX-RPC, rendendo possibile lo sviluppo di applicazioni distribuite Java con protocollo di trasporto SOAP; • 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 modelli di servizi più complicati dove è possibile personalizzare, ad esempio, 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, come nel caso di Zlatan, si parte dalla descrizione dei servizi per la realizzazione di un sistema piuttosto che dalla loro implementazione. I problemi principali di Axis riguardano lo stretto legame con il web application container Tomcat (anche se è possibile con qualche sforzo installare l'ambiente in altri server, come fatto con SJSAS8 in Zlatan) e la conformità soltanto parziale alle specifiche del WS-I. Riassumendo possiamo dire che Axis non è che 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 19 Capitolo 2 Il Web Service 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. 5 - Architettura generale di Axis. 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. 20 3. Preparazione dell’ambiente di sviluppo 3.1 Lato Server Innanzitutto occorre istallare la JDK, la JRE e l’Application Server JAKARTA TOMCAT con la stessa procedura vista a livello client. Come IDE di sviluppo è stato scelto Eclipse: la versione a cui faremo riferimento è la WTP 1.5 AllInOne. Tale sigla sta per Web Tools Project, e contiene, fra l'altro, Eclipse 3.2. In questa versione ci sono già molte funzionalità rivolte al web, come suggerisce il nome. L'installazione è semplicissima, anzi, non necessita neanche di una installazione. Una volta scaricato il file compresso è sufficiente scompattarlo ed Eclipse è già pronto. Al termine dell'estrazione, nella directory "eclipse" (sotto la cartella di destinazione da noi scelta) troviamo tutti i file e le directory coinvolte. È interessante notare come il processo di installazione sia totalmente non invasivo a livello di sistema: in Windows, ad esempio, non vengono inserite chiavi nel registry, non vengono installate dll o file di sistema all'interno delle cartelle /Windows/System32. L'unica accortezza che bisogna avere, prima di utilizzare Eclipse, è quella di avere installato una java virtual machine (ad esempio scaricando il Java SE Development Kit sul sito Sun) che Eclipse stesso possa utilizzare per compilare ed eseguire le applicazioni. L'unica accortezza che bisogna avere, prima di utilizzare Eclipse, è quella di avere installato una java virtual machine (ad esempio scaricando il Java SE Development Kit sul sito Sun) che Eclipse stesso possa utilizzare per compilare ed eseguire le applicazioni. La prima volta che avviamo Eclipse viene chiesta la directory dove salvare i nostri progetti. Fig. 6 Selezionare un workspace 21 Capitolo 3 Preparazione dell’ambiente di sviluppo Attraverso la check box in basso alla finestra di dialogo sarà, quindi, possibile scegliere se utilizzare la medesima directory per tutti i progetti evitando di sceglierla di volta in volta. Una volta ultimata la selezione del workspace, verrà visualizzata una schermata di benvenuto. Fig. 7 – Schermata di benvenuto di Eclipse Il passo successivo prevede l’istallazione di APACHE AXIS che consiste nei due seguenti passi: Fig. 8 - Cartella scompattata di Axis. 22 Capitolo 3 Preparazione dell’ambiente di sviluppo 1) Scompattare Axis, prendere la cartella lib e copiarla all'interno della installazione di Eclipse. 2) Prendere la cartella axis dentro webapps e incollarla sull'omonima cartella di Tomcat. A questo punto abbiamo tutti gli strumenti necessari per creare il nostro Web Service, ma prima di iniziare vediamo la disposizione delle varie librerie: • per un corretto funzionamento dell’applicativo lato server sono necessarie le seguenti librerie nella directory della JDK (nel nostro caso versione 1.5) C:\Programmi\Java\jdk1.5.0\jre\lib\ext.: • dnsns.jar sunjce_provider.jar localedata.jar sunpkcs11.ja le librerie necessarie sotto C:\TOMCAT554\common\lib sono: activation.jar mail.jar commons-el.jar naming-factory-dbcp.jar jasper-compiler jdt.jar naming-factory.jar jasper-compiler.jar naming-resources.jar jasper-runtime.jar ojdbc14.jar jsp-api.jar servlet-api.jar 23 Capitolo 3 • Preparazione dell’ambiente di sviluppo le librerie necessarie sotto C:\TOMCAT554\ webapps\axis\WEB-INF\lib sono: activation.jar jaxrpc.jar axis-ant.jar log4j-1.2.8.jar axis.jar mail.jar commons-discovery- saaj.jar 0.2.jar wsdl4j-1.5.1.jar commons-logging1.0.4.jar Chiarite le posizioni delle varie librerie cominciamo a costruire il nostro web service a partire dalla IDE di sviluppo Eclipse: Innanzitutto selezionare dalla toolbar la voce File->New->Project (figura 1). Nella successiva finestra indicare il tipo di progetto che si vuole realizzare,nel nostro caso un Dinamic Web Project (figura 2). 24 Capitolo 3 Preparazione dell’ambiente di sviluppo Figura 9 – Creazione del Web Service Il primo step per la costruzione di un web service è rappresentato dalla scelta del tipo di progetto tra quelli che eclipse mette a disposizione. 25 Capitolo 3 Preparazione dell’ambiente di sviluppo Figura 10 – Creazione di un Web service Non è difficile riconoscere quale sia effettivamente il tipo di progetto da selezionare per la creazione di un web service: aprire la directory Web e selezionare la voce Dynamic Web Project 26 Capitolo 3 • Preparazione dell’ambiente di sviluppo Successivamente verrà chiesto di dare un nome al nuovo progetto e di lavorare con delle impostazioni. Per il nostro scopo è possibile utilizzare le impostazioni di default e una volta completata la procedura l’interfaccia di eclipse appare nel seguente modo: Figura 11 - L’interfaccia di Eclipse Come ultimo passo non rimane altro che testare il prototipo di applicazione costruito; per fare ciò basta lanciare l’applicazione stessa all’interno di Eclipse attraverso il comando Run. Poiché abbiamo però costruito una web application, occorre definire il server su cui far girare il progetto: Eclipse a questo scopo ci permette di impostare una sorta di server interno che si appoggia all’Application Server istallato sulla macchina di sviluppo, nel nostro caso JAKARTA TOMCAT. Vediamo brevemente come funziona il tutto. Con il tasto destro del mouse ciccare sull’icona del progetto che appare nella Resource Navigator view e scegliere Run As -> Run On Server (figura 12). 27 Capitolo 3 Preparazione dell’ambiente di sviluppo Successivamente selezionare dall’elenco messo disposizione desiderato, il nel a server nostro caso Apache Tomcat v.5.5 (figura 5). Anche in questo caso è possibile modificare delle impostazioni, ma non è controindicativo utilizzare quelle di default. Concludendo tale procedimento guidato l’istallazione del nostro server interno è completata e l’applicazione Figura 12 – Impostare il server di Eclipse (1) è pronta per essere utilizzata (figura 13). Figura 13 Impostare il server di Eclipse (2). E’ necessario selezionare dall’elenco di lo figura stesso Application Server che si è istallato sulla macchina di lavoro. Come sempre facciamo riferimento al nostro caso che utilizza un Tomcat versione 5.5 28 Capitolo 3 Preparazione dell’ambiente di sviluppo Figura 14 – Impostare il server di Eclipse (3) Come ulteriore verifica della funzionalità del server possiamo utilizzare la Console: Figura 15 – La Console di Eclipse Come vedremo più avanti Eclipse ci mette inoltre a disposizione un ulteriore strumento di controllo in fase di utilizzo dei servizi, il TCP/IP Monitor. 29 Capitolo 3 Preparazione dell’ambiente di sviluppo • Ora tutto il nostro sistema è funzionante, ma prima di ultimare il lavoro occorre fare un piccolo passo indietro. Conclusa la fase di editing con la creazione delle classi che implementano i servizi occorre costruire il descrittore dei servizi, il WSDL. Ricordiamo che il WSDL serve a specificare dove si trovano i servizi e le operazioni esposte dal servizio web. L’Eclipse mette a disposizione un meccanismo rapido ed efficiente per la creazione automatica di tale descrittore che fisicamente non è altro che un file xml. Cliccare con il tasto destro del mouse sulla classe nella quale sono definiti i servizi che il nostro progetto realizzare. vuole Selezionando la voce Web Service -> Create Web Service (figura 16) si apre di una finestra impostazioni (figura 17): “spuntare” le due voci Publish the Web service e Monitor the Web service; inoltre se è necessario il TCP/IP Monitor spostare la completamente Successivamente un’ulteriore barra (1) in alto. apparirà finestra nella quale è possibile selezionare Figura 16 – Creazione del Web service (1) tutti i servizi da inserire nel wsdl e scegliere il cosiddetto “Style and use” impostandolo a RPC/Encoded. 30 Capitolo 3 Preparazione dell’ambiente di sviluppo Questa caratteristica rappresenta la scelta del formato di un messaggio SOAP. Le possibili scelte di SOAP: Body di un messaggio SOAP sono: - document: il SOAP Body del documento contiene una o più "parts" che dal punto di vista di XML sono childNodes di SOAP:Body. Non ci sono particolari regole sulla struttura di questi nodi figli. - RPC: il SOAP:Body contiene un elemento il cui nome corrisponde al nome della procedura remota da invocare e un elemento per ogni parametro da fornire alla procedura. La serializzazione del SOAP: Body prevede: - encoded: le regole di serializzazione sono dettate dalla sezione 5 della specifica SOAP 1.1 . - literal: i dati sono serializzati nel rispetto di una specifica XML che ad oggi è XML Schema Definition 1.0, domani potrebbe essere altro. In .NET tutto questo si trasforma nell'utilizzare, come decorazione dei nostri WebMethod, uno dei due seguenti attributi: - SoapRpcMethod: il SOAP:Body sarà RPC/encoded. - SoapDocumentMethod: SOAP:Body in formato document. Se la proprietà Use vale SoapBindingUse.Literal il body sarà document/literal, se invece Use vale SoapBindingUse.Encoded avremo un body document/encoded. Se la proprietà ParameterStyle vale SoapParameterStyle.Bare avremo i parametri inviati tra i SOAP Node posizionati direttamente all'interno del SOAP:Body. Se invece la proprietà ParameterStyle vale SoapParameterStyle.Wrapped i parametri saranno inviati tra i due SOAP node, racchiusi all'interno di un solo elemento, figlio di SOAP:Body. Il default di ASP.NET è Literal, Wrapped. 31 Capitolo 3 Preparazione dell’ambiente di sviluppo Barra (1). Spostare la barra completamente in alto per avere il TCP/IP Monitor Opzioni da selezionare Figura 17 – Impostare il server di Eclipse (2) Risultato di tutta questa procedura è un file .wsdl di cui riportiamo un piccolo frammento: INTESTAZIONE <?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="http://services.jtelemed.it" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://services.jtelemed.it" xmlns:intf="http://services.jtelemed.it" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <!--WSDL created by Apache Axis version: 1.3 Built on Oct 05, 2005 (05:23:37 EDT)--> 32 Capitolo 3 Preparazione dell’ambiente di sviluppo DEFINIZIONE DEI METODI <wsdl:message name="openeventResponse"> <wsdl:part name="openeventReturn" type="xsd:int"/> </wsdl:message> <wsdl:message name="userinfoResponse"> <wsdl:part name="userinfoReturn" type="xsd:string"/> </wsdl:message> <wsdl:message name="listeventsResponse"> <wsdl:part name="listeventsReturn" type="xsd:string"/> </wsdl:message> PORT TYPE <wsdl:portType name="WSRepository"> <wsdl:operation name="echo" parameterOrder="value"> <wsdl:input message="impl:echoRequest" name="echoRequest"/> <wsdl:output message="impl:echoResponse" name="echoResponse"/> </wsdl:operation> <wsdl:operation name="userinfo" parameterOrder="utn_cn username password"> <wsdl:input message="impl:userinfoRequest" name="userinfoRequest"/> <wsdl:output message="impl:userinfoResponse" name="userinfoResponse"/> </wsdl:operation> 33 Capitolo 3 Preparazione dell’ambiente di sviluppo BINDING wsdl:binding name="WSRepositorySoapBinding" type="impl:WSRepository"> wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> wsdl:operation name="echo"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="echoRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://services.jtelemed.it" use="encoded"/> </wsdl:input> <wsdl:output name="echoResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://services.jtelemed.it" use="encoded"/> </wsdl:output> wsdl:operation> SERVIZI <wsdl:service name="WSRepositoryService"> <wsdl:port binding="impl:WSRepositorySoapBinding" name="WSRepository"> <wsdlsoap:address location="http://localhost:8080/jtelemed/services/WSRepository"/> </wsdl:port> </wsdl:service> Infine per testare l’effettivo funzionamento del nostro web service occorre creare un client che si interfacci con i servizi messi a disposizione. Anche in questo caso l’Eclipse ha una via veloce ed efficiente: riprendendo lo schema di figura 5, basta selezionare Generate Sample JSPs. 34 Capitolo 3 Preparazione dell’ambiente di sviluppo Nel Resource Navigator view di Eclipse apparirà una nuova directory: Lanciando server l’applicazione appare una sul Directory Listing For sampleWSRepository dove WSRepository, per noi, è la classe con la quale abbiamo costruito il Web service. La lista è composta da quatto link ai file .jsp evidenziati in figura. Selezionando “TestClient.jsp” si accede alla fase di testing dei servizi. Figura 18 – Creazione del client di test 35 Capitolo 3 Preparazione dell’ambiente di sviluppo 3.2 Fase di deploy Lo schema proposto dalla Sun Microsystem in quanto a Web Applications propone un modello descrittivo dell'alberatura del progetto in modo da separare e descrivere meglio tutta l'applicazione. Questo approccio ha, come sappiamo, gli indubbi vantaggi della standardizzazione. Da una parte consente a chi produce i tool di sviluppo e gli application server di rendersi compatibili (e magari certificati da Sun) con le nuove specifiche, dall'altra permette a chi sviluppa applicazioni Web di sapere che la struttura dell'applicazione deve essere fatta in un certo modo e che un qualunque application server, se certificato, tratterà l'applicazione allo stesso modo, garantendo quindi la portabilità assoluta. Il nodo centrale della standardizzazione, come sappiamo, è dato da una definizione precisa dell'alberatura dell'applicazione. Come sappiamo la root della Web Application dovrà contenere le pagine HTML o JSP. In alcuni casi le pagine JSP possono essere anche nella cartella JSP, in "css" ci sono i fogli di stile, in "js" i file contenenti codice Javascript e dentro "WEB-INF" c'è una determinata alberatura. In particolare a partire dalla cartella "classes" vengono espansi i package Java dell'applicazione, in "conf" ci sono i file deputati alla configurazione applicativa, in "lib" tutte le librerie esterne sotto forma di JAR o ZIP, in "tld" eventuali tag libraries, in "log" i log dell'applicazione. Supponiamo di aver creato la nostra applicazione utilizzando questo schema, ad un certo punto sarà necessario effettuare il deploy su una macchina differente da quella di sviluppo. La macchine di sviluppo, soprattutto se si utilizzano ambienti di sviluppo molto sofisticati, sono configurate diversamente da quelle di test, pertanto il passaggio da un ambiente all'altro potrebbe non essere indolore. Deploy - modo tradizionale Per eseguire il deploy di questa Web Application in modo tradizionale, se si utilizza Tomcat come application server, è necessario copiare l'alberatura così com'è sul file system della macchina server ed editare il file 36 Capitolo 3 Preparazione dell’ambiente di sviluppo TOMCAT_HOME\conf\server.xml aggiungendovi la sezione relativa al nuovo context da creare per poter utilizzare la nuova Web Application attraverso HTTP. La sezione che ci serve è di questo genere: <ContextManager> <Context path="/nomeApplicazione" docBase="c:\nomeApplicazione" crossContext="true" debug="0" reloadable="true" trusted="false" > </Context> </ContextManager> Sarà nostra cura inoltre modificare il CLASSPATH di sistema in modo da includere tutte le librerie esterne che debbano essere utilizzate dalle componenti della nostra applicazione. Questo metodo funziona indipendentemente da come sia organizzata la Web Applicaton. Al termine potremo raggiungere la nostra applicazione attraverso l'url: http://localhost:8080/nomeApplicazione/ Cosa accade però in fase di manutenzione? In altre parole, cosa succede quando dobbiamo riportare in ambiente di test delle modifiche che coinvolgono alcune parti della Web Application? Ci sono solitamente due alternative: • si sovrascrive l'intera alberatura con la versione aggiornata • si sovrascrivono selettivamente soltanto i file modificati In ogni caso gli interventi da effettuare manualmente sono molti e la procedura non troppo semplice, soprattutto se siamo di fronte ad applicazioni con un certo grado di complessità. 37 Capitolo 3 Preparazione dell’ambiente di sviluppo Deploy - il file WAR La specifica proposta da Sun non si limita a descrivere l'alberatura per la costruzione dell'applicazione, ma ci consente anche di avere un buon sistema per effettuare il deploy dell'applicazione utilizzando un singolo file, il file WAR. Un file WAR, come sappiamo, viene generato attraverso il tool jar.exe contenuto nel JDK ed è pertanto un semplice file JAR rinominato. Il fatto che sia un WAR e non un JAR, indipendentemente dal contenuto, gli permette di essere automaticamente riconosciuto come una vera e propria Web Application completa e dotata di vita propria, in questo modo l'application server sarà in grado di autoconfigurarsi sapendo che il WAR contiene esattamente quel tipo di struttura. Per effettuare il deploy su Tomcat di un file WAR è sufficiente crearlo, dopo essersi posizionati all'interno della cartella contenente l’applicazione, attraverso il comando: jar cvf nomeApplicazione.war . Dopo aver ottenuto il file nomeApplicazione.war l'operazione di deploy si limita allo spostamento del file WAR all'interno della cartella webapps di Tomcat. Subito dopo il restart dell'Application Server potremo vedere la nostra applicazione utilizzando il solito url: http://localhost:8080/nomeApplicazione/ Quando l'Application Server parte, si scompatta automaticamente il file WAR e si crea il context per poter raggiungere l'applicazione via http. Utilizzando questo metodo di deploy la manutenzione si riduce ai tre passi seguenti: • creazione del nuovo file WAR • copia del nuovo WAR sulla macchina di test • eliminazione della vecchia alberatura estratta dal WAR precedente Eclipse mette a disposizione un comando per la crazione del file WAR senza dover andare a ricorrere al prompt dei comandi: 38 Capitolo 3 Preparazione dell’ambiente di sviluppo Figura 19 Creazione del WAR file. Cliccare con il tasto destro del mouse sull’icona del progetto e selezionare la voce Export->WAR file. Successivamente verrà richiesta la path di destinazione del file e il nostro WAR è pronto per il deploy. Ora occorre spostare il file WAR appena creato all'interno della cartella webapps di Tomcat. A tal proposito possiamo utilizzare due alternative: • Spostare fisicamente il file nella macchina di sviluppo • Utilizzare il manager del tomcat Indirizzo del server Path del WAR file Fig. 20 - Tomcat Application Manager 39 Capitolo 3 Preparazione dell’ambiente di sviluppo Conclusioni I vantaggi della seconda soluzione sono evidenti, è possibile effettuare un deploy su qualsiasi Application server compatibile con le specifiche di Sun semplicemente copiando il file nella posizione giusta ed effettuando il restart. Utilizzando il metodo tradizionale, invece, dovremmo verificare caso per caso qual è la procedura di creazione del context e specificando manualmente il CLASSPATH. La scelta del WAR è indubbiamente la più interessante. Il vero problema è la migrazione di applicazioni scritte utilizzando strutture differenti e molto spesso questa non è un'operazione per niente banale. 40 4. La comunicazione client-server 4.1 Servizi offerti lato server Funzioni utilizzate lato SERVER Una qualsiasi applicazione JDBC deve: 1. Creare una connessione con il DataBase 2. Creare un oggetto Statement per interrogare il database. 3. Interagire con il database. 4. Gestire i risultati ottenuti. Come detto ogni servizio richiede inizialmente di creare una connessione al DataBase, che viene realizzata tramite il metodo setConnection(). setConnection(): L'API JDBC, racchiusa nel package java.sql.*, fornisce un'interfaccia unificata per l'accesso ad un qualunque database, "mascherando" le peculiarità di ogni singolo DBMS introducendo il concetto di driver. Per capire meglio cosa questo significhi basta dare un'occhiata alla documentazione del pacchetto java.sql: ci si accorge immediatamente che la maggior parte delle funzionalità vengono fornite tramite interfacce (cioè tipi puramente astratti), mentre le classi vere e proprie sono davvero poche. Cosa implica tutto ciò? Semplicemente, l'API JDBC si limita in gran parte a dichiarare le funzionalità che un'interfaccia generalizzata per l'accesso ad un database dovrebbe avere, delegando l'implementazione delle interfacce ai driver che devono fornire l'accesso ai singoli DBMS. Quindi, compito di un driver JDBC è fornire un insieme di classi che implementino tutte le interfacce dichiarate nel pacchetto java.sql. I parametri di connessione vengono recuperati da un file di Properties tramite l’istruzione di load(…). 41 Capitolo 4 La comunicazione client-server Il file DBConfig2 contenente tutti i parametri di connessione ed è situato nella cartella di installazione del TOMCAT, mentre per recuperare informazioni scritte sul file viene usato il comando getProperty(…). Quindi in tal modo è possibile (props.getPropery(“JDBCConnectionURL.1”) recuperare e il l’URL driver di connessione necessario per la connessione(props.getPropery(“JDBCConnectionURL.1”). L’istruzione successiva carica in memoria il driver JDBC per l’accesso al DBMS, infatti le specifiche JDBC redatte dalla Sun prevedono che, per poter essere utilizzato, un driver JDBC deve essere caricato in memoria, e una volta che ciò è avvenuto, il driver stesso ha il compito di registrarsi presso il Driver Manager, il quale ha quindi il compito di tenere traccia dei driver JDBC disponibili, in modo da poter costruire correttamente le istanze delle classi che implementano l'interfaccia java.sql.Connection quando queste vengono richieste dalle applicazioni. Successivamente vengono recuperate dal file di Property la Username per accedere al DB (props.getProperty("UserName.1")) e la password (props.getProperty("UserName.1")). Tramite l’istruzione setPropery() assegno alla stringa jdbc.driver il valore di driver_class, ovvero il driver per la connessione al DB. E’ molto importante settare il Driver in quanto converte chiamate JDBC in chiamate di rete dirette utilizzando protocolli di rete specifici del database (socket), inoltre è semplice da utilizzare in quanto non richiede di avere librerie o software speciali. Ottenere una connessione al database tramite JDBC è abbastanza semplice ed indipendente da quale tipo di DBMS si stia utilizzando. E’ necessario solamente indicare all’applicazione, tramite il DriverManager, quale driver utilizzare e farsi rilasciare un oggetto Connection che servirà per dialogare con la base dati. E’ da notare che per identificare il database è necessario specificare l’URL, ossia l’indirizzo verso il quale tentare la connessione. Questa stringa può variare in base al tipo di driver utilizzato, e, alcune volte, può richiedere anche la specificazione di login e password dell’utente, come nel nostro caso. La classe DriverManager non è l'unica che permette la connessione con un DB, ma è possibile utilizzare anche l'interfaccia Driver che permette di accedere a tutte le informazioni che riguardano il db. JDBC permette di sfruttare una delle caratteristiche piu' importanti dei Database ovvero la gestione delle transazioni. Tale gestione si effettua attraverso due semplici metodi 42 Capitolo 4 La comunicazione client-server della classe Connection: commit e rollback. Il significato dei due metodi e' quello che ci si aspetta: il metodo commit rende definitive tutte le modifiche apportate usando la connessione fino al precedente commit o rollback, viceversa il metodo rollback le annulla fino al precedente commit. All'apertura di una connessione la connessione stessa puo' rendere definitiva ogni singola modifica senza bisogno di chiamare esplicitamente il metodo commit (Auto Commit). Per fare in modo che la semantica dei metodi commit e rollback sia quella descritta prima dobbiamo togliere l'Auto Commit nella connessione usando il metodo setAutoCommit(false). E' chiaro che i metodi per la gestione delle transazioni hanno effetto su tutte e sole le operazioni effettuate sul DB tramite la connessione su cui vengono invocati i metodi stessi. public void setConnection() throws IllegalAccessException , InstantiationException , ClassNotFoundException , SQLException , FileNotFoundException , IOException{ try { Properties props= new Properties(); FileInputStream in = new FileInputStream(System.getProperty("catalina.home")+ File.separator+"DBConfig2"); props.load(in); in.close(); String connectionURL=props.getProperty("JDBCConnectionURL.1"); String driver_class= props.getProperty("JDBCDriver.1"); Class.forName (driver_class).newInstance(); String dbUser= props.getProperty("UserName.1"); String dbPassword= props.getProperty("Password.1"); System.setProperty("jdbc.drivers", driver_class); conn = DriverManager.getConnection(connectionURL, dbUser, dbPassword); conn.setAutoCommit(false); System.out.println("Connected.\n"); } catch (IllegalAccessException e) { System.out.println("Illegal Access Exception: (Open Connection)."); e.printStackTrace(); throw e; } 43 Capitolo 4 La comunicazione client-server catch (InstantiationException e) { System.out.println("Instantiation Exception: (Open Connection)."); e.printStackTrace(); throw e; } catch (ClassNotFoundException e) { System.out.println("Class Not Found Exception: (Open Connection)."); e.printStackTrace(); throw e; } catch (SQLException e) { System.out.println("Caught SQL Exception: (Open Connection)."); e.printStackTrace(); throw e; } catch (FileNotFoundException e) { System.out.println("Caught : FileNotFoundException (Open connection)."); e.printStackTrace(); throw e; } catch (IOException e) { System.out.println("Caught : IOException (Open connection)."); e.printStackTrace(); throw e; } } Come file di Properties abbiamo utilizzato un semplice file di testo all’interno del quale sono settati i valori assegnati alle variabili per la connessione, ovvero il driver, l’URL di connessione, la Username e la Password. String driver_class = "oracle.jdbc.driver.OracleDriver"; String connectionURL = "jdbc:oracle:thin:@172.18.11.201:1521:ora920"; String dbUser="STAGE02"; String dbPassword="STAGE02"; authentication(): Ogni servizio per implementare un maggior livello di sicurezza richiede come parametro username e password. Tramite la funzione authentication() il Web Service va a 44 Capitolo 4 La comunicazione client-server controllare nel proprio data base l’esistenza effettiva dell’utente che ha richiesto di essere loggato. Ciò equivale a realizzare la seguente query: SELECT count(*) from tm_utenti,tm_unitaeroganti WHERE utn_username='"+username+"' AND utn_password='"+password+"' AND utn_codente=uer_codente AND utn_codstruttura=uer_codstruttura AND utn_codspecialita=uer_codspecialita AND utn_codunitaerogante=uer_codunitaerogante Se il risultato è diverso da zero (dovrebbe essere al massimo uguale ad uno), significa che il Web Service ha riconosciuto l’utente come utente autorizzato e gli permette l’accesso all’interno dell’applicazione. userInfo(): Tale metodo restituisce tutte le informazioni relative ad un dato utente. setConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT utn_id, utn_type, utn_codente, utn_codstruttura, utn_codspecialita FROM tm_utenti WHERE utn_cn = " + StringUtil.quoteString(utn_cn)); if (rs.next()) { info = rs.getInt(1) + ";" + rs.getString(2) + ";" + rs.getString(3) + ";" + rs.getString(4)+";"+rs.getString(5); } rs.close(); stmt.close(); conn.close(); Come visto precedentemente la connessione al data base viene effettuato tramite il metodo setConnection(), successivamente viene creato la Statement e il ResultSet a cui viene assegnato il risultato della query. 45 Capitolo 4 La comunicazione client-server I comandi di scrittura come INSERT, UPDATE e DELETE restituiscono un valore che indica quante righe sono state affette (inserite, modificate, cancellate) dall'istruzione. Essi non restituiscono altre informazioni. Le interrogazioni (query) restituiscono un result set (classe ResultSet). È possibile spostarsi nel result set riga per riga (tramite il metodo next()). Si può accedere alle colonne di ogni singola riga chiamandole per nome o per numero. Il result set può essere costituito da un numero qualsiasi di righe. Esso comprende dei metadati che indicano il nome, il tipo e le dimensioni delle colonne. Per le stringhe viene utilizzato il metodo quoteString() della classe StringUtil contenente funzioni di utilità per la loro gestione, soprattutto in fase di interfacciamento con i data base(query e insert). listEvents(double utn_id, double days): Metodo usato dal refertante, che ritorna l’elenco degli eventi che il dottore è in grado di refertare / visualizzare. Essendo un metodo utilizzato dal refertante inizialmente vi è una select che controlla se l’utente è un erogante. In caso positivo viene fatto un controllo sul tipo di esami che l’erogante è in grado di refertare, tramite la seguente query: SELECT per_tes_codice FROM TM_PERMISSIONI WHERE per_utn_id = " + utn_id + ", cioè restituisce il codice che identifica il tipo di esame di possibile refertazione. I valori ottenuti dall’interrogazione devono essere inviati al client per alimentare la griglia., quindi viene creato un file xml contenente il risultato della query. fileName=FileUtil.createXML("SELECT evt_id, evt_dateopen, evt_utn_id, utn_cn, evt_trf_codice, tes_descr, evt_sesso, evt_razza, evt_eta, evt_altezza, evt_um_altezza, evt_peso, evt_um_peso, evt_memo,COUNT(REF_IND) REFERTI FROM TM_EVENTI, TM_TIPOESAMI, TM_UTENTI, TM_REFERTI WHERE evt_utn_id=utn_id AND evt_trf_codice=tes_codice AND evt_trf_codice IN (" +sql_viewexams+ ") AND evt_dateclose IS NULL AND EVT_ID=REF_EVT_ID(+) AND evt_dateopen>=SYSDATE-"+days+" GROUP BY evt_id, evt_dateopen, evt_utn_id, utn_cn, evt_trf_codice, tes_descr, evt_sesso, evt_razza, evt_eta, evt_altezza, evt_um_altezza, evt_peso, evt_um_peso, evt_memo ORDER BY REFERTI","ListEventE",(int)utn_id) 46 Capitolo 4 La comunicazione client-server Ma come è possibile creare un file XML a partire da una query? Il linguaggio di programmazione java, mette a disposizione tramite la libreria javax.xml delle funzioni che permettono di fare ciò, ovvero di incapsulare i risultati della query all’interno di tag XML. Il codice da noi utilizzato per realizzare tale scopo è il seguente: DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance(); DocumentBuilder db=dbf.newDocumentBuilder(); Document doc=db.newDocument(); Element root=doc.createElement("root_element"); //connect to database Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(querySQL); ResultSetMetaData rsmd = rs.getMetaData(); while(rs.next()){ Element row=doc.createElement("row"); for(int j=1;j<=rsmd.getColumnCount();j++){ String colName=rsmd.getColumnName(j); String colValue=rs.getString(j); Element e=doc.createElement(colName); e.appendChild(doc.createTextNode(colValue)); row.appendChild(e); } root.appendChild(row); } doc.appendChild(root); TransformerFactory tmf=TransformerFactory.newInstance(); Transformer tf=tmf.newTransformer(); DOMSource source=new DOMSource(doc); //write to the stream StreamResult result=new StreamResult(fileFullName); tf.transform(source,result); Creato il file Xml contenente il risultato della query, esso viene inviato come allegato tramite il seguente frammento di codice: message=(org.apache.axis.MessageContext.getCurrentContext()).getResponseMessage (); message.addAttachmentPart(message.createAttachmentPart(newDataHandler(new URL("file:"+File.separator+fileNamePath+fileName))));. Essendo un argomento particolarmente delicato quello inerente agli allegati vi torneremo in un secondo momento. 47 Capitolo 4 La comunicazione client-server Inoltre, poichè molti degli altri servizi implementati, utilizzano la tecnica appena vista (creazione del file XML e invio come allegato), non ci soffermeremo dettagliatamente sulla loro descrizione. openEvent(…): Il metodo OpenEvent permette ad un richiedente di inserire un nuovo evento che poi sarà soggetto a refertazione. E’ uno dei pochi casi in cui non c’è la creazione del file xml e il suo funzionamento è semplicissimo: • Ha come parametri le informazioni necessarie al nuovo evento • Prende tali parametri e fa un “INSERT” all’interno del database Ora poiché la tabella oggetto dell’insert ha un campo BLOB (Binary Large OBject) destinato a ricevere la codifica binaria dell’allegato (la documentazione relativa ad un esame, eg. immagine di una radiografia o di una tac ecc…) abbiamo la necessità di una particolare gestione: • Occorre dapprima inserire un “EMPTY_BLOB” nel campo BLOB della tabella (FASE 1) • Selezionare tramite una query l’EMPTY_BLOB appena inserito (FASE 2) • Recuperare il vero BLOB come attachment (FASE 3) • Sovrascrivere l’EMPTY_BLOB con l’effettivo BLOB (FASE 4) Vediamo come tutto questo viene realizzato: FASE 1: setConnection(); stmt = conn.createStatement(); stmt.execute("INSERT INTO tm_eventi (evt_utn_id, evt_trf_codice, evt_dateopen, evt_codice_esterno, evt_sesso, evt_eta, evt_altezza, evt_um_altezza, evt_peso, evt_um_peso, evt_memo, evt_nomeallegato, evt_allegato) VALUES (" + utn_id + "," + StringUtil.quoteString(evt_trf_codice) + ",TO_DATE('" + DateTime.getCurrentDateTime()+"','YYYY-MM-DD HH24:MI:SS')," + StringUtil.quoteString(evt_codice_esterno) + "," + 48 Capitolo 4 La comunicazione client-server StringUtil.quoteString(evt_sesso) + "," + evt_eta + "," + evt_altezza + "," + StringUtil.quoteString(evt_um_altezza) + "," + evt_peso + ","+ StringUtil.quoteString(evt_um_peso) + "," + StringUtil.quoteString(evt_memo)+","+ StringUtil.quoteString(evt_nomeallegato.trim())+ ",EMPTY_BLOB())"); stmt.close(); FASE 2: stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT MAX(EVT_ID) FROM TM_EVENTI WHERE EVT_UTN_ID="+ utn_id); if (rs.next()) evt_id=Integer.parseInt(rs.getString(1)); stmt.close(); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT EVT_ALLEGATO FROM TM_EVENTI WHERE EVT_ID="+evt_id+" FOR UPDATE NOWAIT"); FASE 3: if (rs.next()) { oracle.sql.BLOB bcol = ((OracleResultSet)rs).getBLOB(1); OutputStream blobOutputStream = bcol.getBinaryOutputStream(); //Recupera allegato dal messaggio di richiesta del client message=(org.apache.axis.MessageContext.getCurrentContext()).getRequestMessage( ); Iterator attachments = message.getAttachments(); //Se non ci sono attachment ritorna -5 if(!attachments.hasNext())return -5; //Prendo solo il primo allegato att = (AttachmentPart) attachments.next(); DataHandler dh = att.getActivationDataHandler(); //Ottengo la codifica in byte dell'allegato BufferedInputStream bis = new BufferedInputStream(dh.getInputStream()); byte[] bs = new byte[bis.available()]; bis.read(bs, 0, bs.length); bis.close(); FASE 4: //Inserisco la codifica in byte dell'allegato nel campo BLOB del DB blobOutputStream.write(bs, 0, bs.length); blobOutputStream.flush(); blobOutputStream.close(); System.out.println("Generato nuovo evento con id "+(int)evt_id+"."); } stmt.close(); conn.close(); 49 Capitolo 4 La comunicazione client-server closeEvent(int evt_id): Tale metodo aggiorna lo stato dell’evento a chiuso, ovvero l’utente richiedente ha deciso che non ha più bisogno di ulteriori refertazioni. Per fare ciò va a valorizzare il campo evt_dateclose della tabella eventi con la data corrente: setConnection(); Statement stmt = conn.createStatement(); stmt.execute("UPDATE tm_eventi SET evt_dateclose = TO_DATE('" + DateTime.getCurrentDateTime()+"','YYYY-MM-DD HH24:MI:SS') WHERE EVT_ID= "+evt_id); System.out.println("Evento "+(int)evt_id+" chiuso con successo."); stmt.close(); conn.close(); downloadEvent(double evt_id): Permette ad un client (refertante) di scaricare la documentazione relativa ad un dato evento che egli intende refertare. La procedura consta di 4 passi: • Recupero dal database del nome del file relativo all’evento richiesto dal client • Lettura dal database del BLOB corrispondente all’evento richiesto • Scrittura del BLOB in un file fisico • Invio del file appena creato come attachment setConnection(); //Recupero nome del file Statement stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT TRIM(EVT_NOMEALLEGATO) EVT_NOMEALLEGATO FROM TM_EVENTI WHERE EVT_ID= "+evt_id); if (rs.next()) fileName=rs.getString(1); stmt.close(); String fileFullName=fileNamePath+fileName.trim(); //Recupero del BLOB stmt = conn.createStatement(); 50 Capitolo 4 La comunicazione client-server rs = stmt.executeQuery("SELECT EVT_ALLEGATO FROM TM_EVENTI WHERE EVT_ID= "+evt_id); if (rs.next()) { oracle.sql.BLOB bcol =((OracleResultSet)rs).getBLOB(1); //Scrittura del blob su un file fisico long inizio=1; dati = bcol.getBytes(inizio,(int)bcol.length()); FileOutputStream inn=new FileOutputStream(fileFullName); for(int i=0;i<dati.length;i++){ inn.write(dati[i]); } inn.close(); //Invio del file come attachment message=(org.apache.axis.MessageContext.getCurrentContext()).getResponseM essage(); /*LINUX*/ message.addAttachmentPart(message.createAttachmentPart(new DataHandler(new URL("file:"+File.separator+File.separator+fileNamePath+fileName)))); /*WINDOWS*/ //message.addAttachmentPart(message.createAttachmentPart(new DataHandler(new ù URL("file:"+File.separator+fileNamePath+fileName)))); //Cancellazione del file dalla cartella temp del Tomcat FileUtil.deleteFiles(); System.out.print("Download documentazione evento "+(int)evt_id+" scaricata con successo."); } stmt.close(); conn.close(); referta(…): Analogo ad openEvent(), esso inserisce nella tabella dei referti l’evento appena refertato. Anche in tal caso è presente un attachment, quindi un campo BLOB (documentazione del referto) che deve essere gestito allo stesso modo a quanto fatto precedentemente. Vediamo com’è stata strutturata la query d’inserimento: setConnection(); stmt = conn.createStatement(); String query="INSERT INTO tm_referti (ref_evt_id, ref_utn_id, ref_datereferto, ref_nomeallegato_referto, ref_allegato_referto, ref_memo, ref_definitivo) VALUES (" + ref_evt_id + "," + ref_utn_id + ",TO_DATE('" + DateTime.getCurrentDateTime()+"','YYYY-MM-DD HH24:MI:SS'),"+ StringUtil.quoteString(ref_nomeallegato_referto.trim()) + ",EMPTY_BLOB()," + StringUtil.quoteString(ref_memo)+","+ StringUtil.quoteString(ref_definitivo)+")"; 51 Capitolo 4 La comunicazione client-server stmt.execute(query); stmt.close(); listEventReferted(double utn_id): Visualizza gli eventi refertati relativi ad un specificato utente. E’ costituito dalla solita struttura: Creazione xml Invio come attachment Ci soffermiamo alla query con la quale viene creato l’xml: fileName=FileUtil.createXML("SELECT evt_id, evt_trf_codice, tes_descr, evt_dateopen,evt_codice_esterno, COUNT(REF_IND) REFERTI FROM TM_EVENTI, TM_TIPOESAMI, TM_UTENTI, TM_REFERTI WHERE evt_utn_id=utn_id AND evt_trf_codice=tes_codice AND evt_dateclose IS NULL AND EVT_ID=REF_EVT_ID(+) AND utn_id="+utn_id+" GROUP BY evt_id, evt_trf_codice, tes_descr, evt_dateopen,evt_codice_esterno ORDER BY REFERTI DESC","ListEventR",(int)utn_id); listEventRefertedByEvtId(double ref_evt_id): Permette di visualizzare tutte le refertazioni relative ad un dato evento (ogni evento può avere più di una refertazione). Anch’esso si basa sul binomio creazione xml e invio come attachment: fileName=FileUtil.createXML("SELECT REF_IND,REF_UTN_ID, UTN_CN, REF_DATEREFERTO,TES_DESCR,REF_NOMEALLEGATO_REFERTO,REF_MEMO,REF_DEFINIT IVO FROM TM_EVENTI,TM_REFERTI, TM_UTENTI, TM_TIPOESAMI WHERE EVT_TRF_CODICE=TES_CODICE AND UTN_ID=REF_UTN_ID AND REF_EVT_ID="+ref_evt_id+" AND EVT_ID=REF_EVT_ID","ListRefertByEvtId",(int)ref_evt_id); 52 Capitolo 4 La comunicazione client-server downloadRefert(double ref_ind): Così come un refertante scarica la documentazione relativa all’evento che egli vuole refertare, allora alla stessa maniera un utente richiedente a refertazione ultimata chiede di scaricare il referto relativo alla sua richiesta. setConnection(); //Recupero nome del file Statement stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT TRIM(REF_NOMEALLEGATO_REFERTO) REF_NOMEALLEGATO_REFERTO FROM TM_REFERTI WHERE REF_IND= "+ref_ind); if (rs.next()) fileName=rs.getString(1); stmt.close(); String fileFullName=fileNamePath+fileName.trim(); stmt = conn.createStatement(); //Recupero del BLOB rs = stmt.executeQuery("SELECT REF_ALLEGATO_REFERTO FROM TM_REFERTI WHERE REF_IND= "+ref_ind); if (rs.next()) { oracle.sql.BLOB bcol =((OracleResultSet)rs).getBLOB(1); //Scrittura del blob su un file fisico long inizio=1; dati = bcol.getBytes(inizio,(int)bcol.length()); FileOutputStream inn=new FileOutputStream(fileFullName); for(int i=0;i<dati.length;i++){ inn.write(dati[i]); } inn.close(); //Invio del file come attachment message=(org.apache.axis.MessageContext.getCurrentContext()).getResponseM essage(); /*LINUX*/ message.addAttachmentPart(message.createAttachmentPart(new DataHandler(new URL("file:"+File.separator+File.separator+fileNamePath+fileName)))); /*WINDOWS*/ //message.addAttachmentPart(message.createAttachmentPart(new DataHandler(new URL("file:"+File.separator+fileNamePath+fileName)))); System.out.print("Download referto "+(int)ref_ind+" effettuato con successo."); //Cancellazione del file dalla cartella temp del Tomcat FileUtil.deleteFiles(); } stmt.close(); conn.close(); 53 Capitolo 4 La comunicazione client-server showStoric(double utn_id): Permette di visualizzare lo storico degli eventi di un determinato utente, ovvero gli eventi che l’utente stesso ha deciso di chiudere. Anch’esso a partire da una query genera un file xml che poi verrà inviato come attachment. La query è la seguente: SELECT evt_id, evt_trf_codice, tes_descr, evt_dateopen,evt_dateclose, evt_nomeallegato, evt_codice_esterno, evt_memo,COUNT(REF_IND) REFERTI FROM TM_EVENTI, TM_TIPOESAMI, TM_UTENTI, TM_REFERTI WHERE evt_utn_id=utn_id AND evt_trf_codice=tes_codice AND evt_dateclose IS NOT NULL AND EVT_ID=REF_EVT_ID(+) AND utn_id="+utn_id+" GROUP BY evt_id, evt_trf_codice, tes_descr, evt_dateopen,evt_dateclose, evt_nomeallegato, evt_codice_esterno, evt_memo ORDER BY REFERTI Il filtro fondamentale è la presenza di una data di chiusura non nulla. listRefertForErogants(double utn_id, double days): E’ un servizio destinato ad un utente refertante e permette di visualizzare i referti da egli effettuati che sono ancora aperti, ovvero che non sono stati dichiarati, sempre dallo stesso refertante, definitivi. Inoltre tali referti sono filtrati secondo una specifica data, cioè il refertante può consultare solamente quei referti che hanno una data di refertazione che rientri in un range stabilito dalla data corrente e dai giorni selezionati da lui stesso nell’interfaccia di visualizzazione. Tramite una query viene creato un file xml che poi viene inviato al client come allegato. Query: SELECT ref_ind, ref_evt_id, evt_utn_id, utn_cn, evt_trf_codice, tes_descr, ref_datereferto, ref_definitivo FROM TM_EVENTI, TM_TIPOESAMI, TM_UTENTI, TM_REFERTI WHERE evt_utn_id=utn_id AND evt_trf_codice=tes_codice AND evt_trf_codice IN (SELECT per_tes_codice FROM TM_PERMISSIONI WHERE per_utn_id = ref_utn_id) AND evt_dateclose IS NULL AND EVT_ID=REF_EVT_ID(+) AND ref_datereferto>=SYSDATE-"+days+" AND ref_utn_id="+utn_id+" ORDER BY REF_DEFINITIVO 54 Capitolo 4 La comunicazione client-server refertToEvent(double ref_ind): Permette di visualizzare le informazioni di interesse relative alla richiesta di un evento che è stato gia refertato, ad esempio tipo e codice esame, caratteristiche fisiche del richiedente, informazioni sul referto. Tale servizio viene invocato nel momento in cui un utente erogante (refertante) decide di consultare la refertazione di un evento che ha già subito delle refertazioni da parte dell’utente stesso o da altri utenti autorizzati. In tal caso vedrà visualizzate oltre alle informazioni dell’evento anche la diagnosi con tanto di referto allegato. Tutte le informazioni verranno visualizzate lato client in una griglia alimentata tramite un file xml creato attraverso la seguente query: SELECT ref_evt_id, evt_trf_codice, tes_descr, ref_definitivo, ref_memo, evt_sesso, evt_eta, evt_peso,evt_um_peso,evt_altezza,evt_um_altezza,evt_memo, ref_nomeallegato_referto FROM TM_EVENTI, TM_TIPOESAMI, TM_UTENTI, TM_REFERTI WHERE evt_utn_id=utn_id AND ref_evt_id=evt_id AND evt_trf_codice=tes_codice AND evt_trf_codice IN (SELECT per_tes_codice FROM TM_PERMISSIONI WHERE per_utn_id = ref_utn_id) AND evt_dateclose IS NULL AND EVT_ID=REF_EVT_ID(+) AND ref_ind="+ref_ind updateRefert(double ref_ind, ….): Consente ad un utente refertante di poter modificare un qualsiasi suo referto dichiarato non definitivo. E’ analogo al servizio “referta”, l’unica differenza sta nella query: si usa un UPDATE anziché INSERT. setConnection(); stmt = conn.createStatement(); String query="UPDATE tm_referti SET" + " REF_NOMEALLEGATO_REFERTO="+StringUtil.quoteString(ref_nomeallegato_referto.trim ()) + "," + " REF_MEMO="+StringUtil.quoteString(ref_memo)+ "," + " REF_DEFINITIVO="+StringUtil.quoteString(ref_definitivo) + ","+ " REF_DATEREFERTO=TO_DATE('"+DateTime.getCurrentDateTime()+"','YYYY-MM-DD HH24:MI:SS')"+ " WHERE REF_IND="+ref_ind; stmt.execute(query); stmt.close(); 55 Capitolo 4 La comunicazione client-server Per il campo BLOB viene utilizzata la stessa procedura ampliamente descritta in alcuni dei servizi precedenti. updateRefertWithoutBLOB(double ref_ind, ….): E’ il servizio precedente senza però l’update del campo BLOB. login(String user, String pswd): Il servizio di login è molto simile al servizio “authentication”, ma leggermente più comleto. Infatti oltre ad andare a verificare l’esistenza dell’utente che ha richiesto l’accesso all’applicazione sul database, esso, in caso affermativo, invia al client un file xml contenete le informazioni principali dell’utente appena loggato, cosicché queste possano essere visualizzate nell’interfaccia migliorando l’estetica e la leggibilità. Tramite una query si reperiscono le informazioni dell’utente che vengono poi incapsulate in un file xml e inviate come allegato al client. Query: String query="SELECT utn_id,utn_cn,utn_type,utn_codente,utn_codstruttura,utn_codspecialita,u tn_codunitaerogante,uer_descr from tm_utenti,tm_unitaeroganti WHERE utn_username='"+user+"' AND utn_password='"+pswd+"' AND utn_codente=uer_codente AND utn_codstruttura=uer_codstruttura AND utn_codspecialita=uer_codspecialita AND utn_codunitaerogante=uer_codunitaerogante"; 56 Capitolo 4 4.2 La comunicazione client-server Axis Attachment LATO SERVER a) Aggiungere un allegato al messaggio SOAP di risposta al client (upload) import java.io.*; import java.net.URL; import javax.activation.DataHandler; import javax.xml.soap.SOAPMessage; import org.apache.axis.attachments.AttachmentPart; // ……Servizio del Web Services…… SOAPMessage message=null; message=(org.apache.axis.MessageContext.getCurrentContext()).getResponseMessage(); URL url= new URL("file:"+File.separator+fileNamePath+fileName) message.addAttachmentPart(message.createAttachmentPart(new DataHandler(url)); Poiché si vuole allegare un file al messaggio di risposta del server verso il client, si deve recuperare il Context (contesto) corrente e quindi il relativo messaggi di risposta (getResponseMessage()). Per org.apache.axis.MessageContext getResponseMessage(). La realizzare e, classe in ciò si utilizza ordine, i metodi SOAPMessage la classe di Axis getCurrentContext() implementa il e metodo addAttachmentPart che permette, individuato l’URL del file e creata la classe DataHandler, di creare una sezione del messaggio SOAP in cui posizionare effettivamente l’allegato. 57 Capitolo 4 La comunicazione client-server b) Recuperare un allegato dal messaggio SOAP di richiesta dal client (download) Codice import java.io.*; import java.net.URL; import javax.activation.DataHandler; import javax.xml.soap.SOAPMessage; import org.apache.axis.attachments.AttachmentPart; //……Servizio del Web Services…… message=(org.apache.axis.MessageContext.getCurrentContext()).getRequestMessage(); Iterator attachments = message.getAttachments(); if(!attachments.hasNext())return -5; //Se non ci sono attachment ritorna -5 att = (AttachmentPart) attachments.next(); //Prendo solo il primo allegato DataHandler dh = att.getDataHandler(); BufferedInputStream bis = new BufferedInputStream(dh.getInputStream()); byte[] bs = new byte[bis.available()]; bis.read(bs, 0, bs.length); bis.close(); Poiché si vuole recuperare un allegato dal messaggio di richiesta del client verso il server, si deve recuperare il Context (contesto) corrente e quindi il relativo messaggi di richiesta (getRequestMessage()). La classe SOAPMessage mette a disposizione il metodo getAttachments() che restituisce un iterator degli allegati. Si recupera, quindi, il primo allegato (primo elemento dell’iterator) ed il relativo DataHandler attraverso il metodo getDataHandler(). A questo punto si attivano i canali di stream per la successiva scrittura del file sul file system. 58 Capitolo 4 La comunicazione client-server TCP/IP Monitor Abbiamo più volte parlato del TCP/IP Monitor come uno strumento per il controllo dello scambio di messaggi tra client e server. Vediamo come appare la sua interfaccia: Ora di invio della richiesta, tempo di risposta, protocollo utilizzato Richiesta del client Struttura del messaggio SOAP contenente la richiesta da parte del client di un determinato servizio. Risposta del server Struttura del messaggio SOAP contenente la risposta da parte del server al servizio richiesto dal client. La presenza di un attachement si nota dalla parola chiave _Part_ seguita da un numero di serie. In questo caso il server ha risposto al client inviando un determinato file (nella maggioranza dei casi xml) come allegato. 59 5. Struttura del database 5.1 Specifica delle operazioni Il modello base impostato ha come concetti di riferimento l’Utente Richiedente, oggetto del servizio di assistenza e l’Utente Erogante, soggetti che erogano il servizio di refertazione. Il richiedente (accreditato) che si interfaccia con questo applicativo deve avere la possibilità di fare una richiesta “generica” (senza un destinatario definito) pubblicando il risultato di una indagine clinica. A questo punto un qualunque refertante accreditato può interagire con il nostro applicativo e fornire una risposta al richiedente. Se il richiedente è soddisfatto della refertazione ottenuta può chiudere il processo di pubblicazione, altrimenti lasciare pubblicata la richiesta in attesa di refertazione da parte di refertanti diversi. 60 Capitolo 5 Struttura del database 5.2 Specifiche di progetto Gli utenti Database presenti sono: • ENTE RICHIEDENTE • ENTE EROGANTE Innanzitutto nella pagina iniziale è presente un area di accesso riservato in cui vi è l’identificazione dell’utente richiedente o dell’utente che emette il referto. In base all’utente che si autentica viene fuori una maschera diversa. Nella maschera relativa all’ utente RICHIEDENTE è presente una sorta di menù contenente la gestione delle varie richieste, ossia le informazioni relative alla nuova richiesta, lo stato delle richieste non chiuse ovvero il messaggio di consulta delle 61 Capitolo 5 Struttura del database richieste non chiuse e uno storico delle richieste chiuse entro due mesi dalla data in cui la richiesta viene chiusa. Operazioni possibili di un utente richiedente Informazioni utente: nome e tipo Per inserire un nuovo evento il richiedente deve compilare un modulo in cui inizialmente seleziona la lingua (Italiano/Inglese). Successivamente inserisce tutte le informazioni personali quali sesso, età, altezza e peso. Altre informazioni necessarie per identificare la richiesta sono: Riferimento della richiesta, il tipo di riferimento, un campo note (per eventuali chiarimenti) e un campo Allegati in cui è possibile inserire degli allegati, ovvero documenti digitali relativi agli esami effettuati. 62 Capitolo 5 Struttura del database Figura – Maschera di inserimento di un nuovo evento Cliccando sulla voce “Eventi refertati” vengono visualizzati tutti gli eventi per cui l’utente richiedente appena loggato ha richiesto una refertazione. Numero di referti per un dato evento Link ai dettagli di un dato evento Possibilità di chiudere un evento Selezionando invece “Eventi chiusi” appare una maschera simile alla precedente nella quale sono elencati tutti gli eventi generati da un dato utente che per qualche motivo sono 63 Capitolo 5 Struttura del database stati chiusi, ovvero l’utente ha deciso che non è più interessato ad alcuna refertazione o ad alcuna ulteriore refertazione. Un evento chiuso non è più visibile da un refertante. Nella maschera relativa all’utente che effettua il referto è presente un menù contenente un link ad una lista degli eventi aperti ed uno ai referti non definitivi. Quando parliamo di lista di referti o lista di eventi aperti bisogna specificare che essi sono tutti quelli del tipo per cui un utente erogante è specializzato, ovvero, per esempio, se l’utente è un radiologo egli potrà refertare solo esami di radiologia (e non ad esempio quelli di virologia). Tali liste potranno essere consultate dai refertanti i quali in seguito potranno dare un loro parere. 64 Capitolo 5 Struttura del database Operazioni possibili di un utente erogante Informazioni utente: nome e tipo Filtro per data Figura – Maschera di visualizzazione degli eventi aperti Per inserire un referto è necessario cliccare sull’evento (nell’esempio il 469); si aprirà una finestra con tutti i dettagli e con la possibilità di scaricare la documentazione relativa all’evento. 65 Capitolo 5 Struttura del database Successivamente occorre compilare un modulo che ha la seguente forma: Compilato il modulo e inserito il referto come allegato per inoltrarlo al servar basta premere il pulsante “Invia referto”. 66 Capitolo 5 Struttura del database Infine, selezionando dal menù la voce “Referti aperti”, l’utente erogante può consultare le refertazioni che lui stesso ha emesso o che sono in fase di emissione (refertazione non definitiva). Sempre dal menù sulla sinistra è possibile disconnettersi e tornare alla pagina iniziale: 67 Capitolo 5 Struttura del database 5.3. Progettazione concettuale della base di dati Abbiamo seguito una strategia mista come andremo a descrivere. 5.3.1 Un primo schema scheletro (top-down) Sono state identificate inizialmente le seguenti entità (che hanno vita propria e indipendente l’una delle altre): Utente Richiedente e Utente Refertante. Ad un primo livello di astrazione è concepito il seguente schema scheletro: 5.3.2 Sviluppo delle componenti dello scheletro (inside-out) Utente Sia l’Utente Richiedente che l’Utente Refertante appartengono a delle strutture cliniche accreditate e si differenziano tra di loro solo per il ruolo che ricoprono nell’interfacciarsi con l’applicativo. Si può pensare, quindi, di accorpare queste due entità in una sola entità e sfruttare una relazione ricorsiva. Ogni Utente è caratterizzato da: - un identificativo univoco dell’utente all’interno del sistema , ID - un CommonName che deve essere presente anche all’interno del certificato digitale, CN - un tipo utente (R=utente richiedente, E=utente erogante), TYPE - un codice identificativo dell’ente, CODENTE - un codice identificativo della struttura, CODSTRUTTURA - un codice della specialità della struttura, CODSPECIALITA - un codice identificativo dell’unità erogante, CODUNITAEROGANTE - una username dell’ utente per l’accesso al repository, USERNAME - una password dell’ utente per l’accesso al repository, PASSWORD 68 Capitolo 5 Struttura del database La chiave primaria per questa entità è l’ID univoco. Un utente richiedente può emettere una o più richieste di refertazione [cardinalità (1,N)] mentre un utente erogante può rispondere alle richieste di nessuno o più richiedenti [cardinalità (0,N)] Unità eroganti Ogni utente (richiedente o refertante) appartiene ad una unità erogante che è caratterizzata da: • un identificativo dell’ente, CODENTE • un identificativo della struttura, CODSTRUTTURA • un identificativo della specialità, CODSPECIALITA • un codice dell’unità erogante, CODUNITAEROGANTE • una descrizione dell’unità erogante, DESCR Sono chiave primaria il CODENTE, il CODSTRUTTURA, il CODSPECIALITA ed il CODUNITAEROGANTE. 69 Capitolo 5 Struttura del database Tipo_Esame E’ possibile definire, a questo punto, un’entità Tipo_Esame che tiene traccia di tutte le possibili patologie. Ogni patologia è individuata da: - un codice identificativo della patologia dell’esame, CODICE - una descrizione della patologia dell’esame, DESCR Evento Ogni laboratorio accreditato che richiede una refertazione ad un altro laboratorio genera un evento. Le informazioni necessarie per tale evento sono: - identificativo univoco dell’evento all’interno del sistema, ID - identificativo univoco del richiedente, UTN_ID - tipologia dell’esame collegato all’evento, TRF_CODICE - data e ora di creazione dell’evento, DATEOPEN - data e ora di chiusura dell’evento, DATECLOSE 70 Capitolo 5 Struttura del database - il file allegato all’evento, ALLEGATO - nome del file allegato, NOMEALLEGATO - codice di ricerca esterna, CODICE_ESTERNO - dati aggiuntivi del paziente: RAZZA, SESSO, ETA, ALTEZZA, UM_ALTEZZA, PESO, UM_PESO, MEMO Un utente può generare uno o più eventi (richiesta di refertazione) mentre un evento è legato ad uno ed un solo utente [cardinalità: (1,N) ; (1,1)]. Ad ogni evento, invece, è associata un’unica patologia. Il richiedente deve essere un utente accreditato e quindi già registrato nella tabella UTENTI: per questo motivo UTN_ID è chiave esterna con l’ ID della tabella UTENTI. La tipologia di esame deve essere una di quelle previste dalla tabella TIPO_ESAMI, perciò TRF_CODICE è chiave esterna con il CODICE della tabella TIPO_ESAMI. dateclose 71 Capitolo 5 Struttura del database Referto Quando un utente refertante fornisce una risposta ad un utente richiedente genera un referto. Le informazioni necessarie per tale referto sono: - indice progressivo del referto dell’evento, IND - identificativo univoco dell’evento all’interno del sistema, EVT_ID - identificativo dell’utente che ha scritto il referto, UTN_ID - file che contiene il referto in formato testo, ALLEGATO_REFERTO - nome del file del referto, NOMEALLEGATO_REFERTO - data e ora di inserimento del referto, DATEREFERTO Un referto è la risposta ad un determinato evento: perciò EVT_ID è chiave esterna con l’ID dell’evento. L’utente che ha effettuato il referto deve essere un ente accreditato e già registrato nella tabella UTENTI: per questo motivo UTN_ID è chiave esterna con l’ID della tabella UTENTI. Si noti che ad un evento possono essere associati più referti (in generale potranno essere forniti referti ad un evento finché l’evento non viene chiuso). 72 Capitolo 5 Struttura del database Permissioni Ogni utente refertante potrà eseguire refertazioni solo ed esclusivamente sulle patologie di competenza, ovvero solo per le patologie a lui accreditate. Per questo motivo si inserisce un’entità PERMISSIONI che, per ogni utente identifica i tipi referti a lui competenti. Tale entità si compone di due campi: - identificatore dell’utente, IDUTE (che è chiave primaria e chiave esterna con l’ID della tabella UTENTI) - identificatore della patologia di competenza, CODESAME 73 Capitolo 5 Struttura del database Schema E-R completo dateclose 74 Capitolo 5 Struttura del database 5.4 Traduzione verso il modello relazionale Tabella TM_UTENTI Nome del campo Tipo Dim. Identificatore UTN_ID (Autonumber) Number 10 UTN_CN Varchar 128 UTN_TYPE Varchar 1 UTN_CODENTE Varchar 10 UTN_CODSTRUTTURA Varchar 10 UTN_CODSPECIALITA Varchar 10 UTN_CODUNITAEROGANTE Varchar 10 UTN_USERNAME Varchar 208 UTN_PASSWORD Varchar 20 Chiave esterna Primary Key TM_UNITAEROGANTI. UER_CODENTE TM_UNITAEROGANTI. UER_CODSTRUTTURA TM_UNITAEROGANTI. UER_CODSPECIALITA TM_UNITAEROGANTI. UER_CODUNITAEROGANTE Tabella TM_UNITAEROGANTI Nome del campo Tipo Dimensione Identificatore UER_CODENTE Varchar 10 Primary Key UER_CODSTRUTTURA Varchar 10 Primary Key UER_CODSPECIALITA Varchar 10 Primary Key UER_CODUNITAEROGANTE Varchar 10 Primary Key UER_DESC Varchar 50 Chiave esterna 75 Capitolo 5 Struttura del database Tabella TM_TIPOESAMI Nome del campo Tipo Dimensione Identificatore TES_CODICE Varchar 10 TES_DESCR Varchar 255 Chiave esterna Primary Key Tabella TM_EVENTI Nome del campo Tipo Dimensione Identificatore Chiave esterna EVT_ID (Autonumber) Number 10 Primary Key EVT_UTN_ID Number 10 TM_UTENTI.UTN_ID EVT_TRF_CODICE Varchar 10 TM_TIPOESAMI.TES_CODICE EVT_DATEOPEN Date EVT_DATECLOSE Date EVT_ALLEGATO Blob EVT_NOMEALLEGATO Varchar 25 EVT_CODICE_ESTERNO Varchar 10 EVT_RAZZA Varchar 25 EVT_SESSO Varchar 1 EVT_ETA Number 3 EVT_ALTEZZA Number (8,4) EVT_UM_ALTEZZA Varchar 10 EVT_PESO Number (8,4) EVT_UM_PESO Varchar 10 EVT_MEMO Varchar 255 Tabella TM_REFERTI 76 Capitolo 5 Struttura del database Nome del campo Tipo Dimensione Identificatore Chiave esterna REF_IND (Autonumber) Number 10 REF_EVT_ID Number 10 TM_EVENTI.EVT_ID REF_UTN_ID Number 10 TM_UTENTI.UTN_ID REF_ALLEGATO_REFERTO Blob REF_NOMEALLEGATO_REFERTO Varchar REF_DATEREFERTO Primari Key 25 Date Tabella TM_PERMISSIONI Nome del campo Tipo Dim. Identificatore Chiave esterna PER_UTN_ID Number 10 Primary Key TM_UTENTI.UTN_ID PER_TES_CODICE Varchar 10 Primary Key TM_TIPOESAMI.TES_CODICE 77 Capitolo 5 Struttura del database 5.5 Codifica SQL CREATE TABLE TM_UNITAEROGANTI ( uer_codente VARCHAR(10) DEFAULT '' NOT NULL, uer_codstruttura VARCHAR(10) DEFAULT '' NOT NULL, uer_codspecialita VARCHAR(10) DEFAULT '' NOT NULL, uer_codunitaerogante VARCHAR(10) DEFAULT '' NOT NULL, uer_descr VARCHAR(50) DEFAULT NULL, PRIMARY KEY (uer_codente,uer_codstruttura,uer_codspecialita,uer_codunitaerogante) ); -----------------------------------------------------------------------------------------CREATE TABLE TM_TIPOESAMI ( tes_codice VARCHAR(10) DEFAULT '', tes_descr VARCHAR(255) DEFAULT '' NOT NULL, PRIMARY KEY (tes_codice) ); -----------------------------------------------------------------------------------------CREATE TABLE tm_utenti ( utn_id NUMBER(10) PRIMARY KEY, utn_cn VARCHAR(128) DEFAULT NULL UNIQUE, utn_type VARCHAR(1) DEFAULT 'M' NOT NULL, utn_codente VARCHAR(10) DEFAULT NULL, utn_codstruttura VARCHAR(10) DEFAULT NULL, utn_codspecialita VARCHAR(10) DEFAULT NULL, utn_codunitaerogante VARCHAR(10) DEFAULT NULL, utn_username VARCHAR(20), utn_password VARCHAR(20), FOREIGN KEY (utn_codente, utn_codstruttura, utn_codspecialita, utn_codunitaerogante) REFERENCES TM_UNITAEROGANTI(UER_CODENTE, UER_CODSTRUTTURA, UER_CODSPECIALITA, UER_CODUNITAEROGANTE) ); CREATE SEQUENCE TM_UTENTI_SEQ START WITH 1 INCREMENT BY 1 NOMAXVALUE; CREATE TRIGGER TM_UTENTI_TRIGGER BEFORE INSERT ON TM_UTENTI FOR EACH ROW BEGIN SELECT TM_UTENTI_SEQ.NEXTVAL INTO :NEW.UTN_ID FROM DUAL; END; -----------------------------------------------------------------------------------------78 Capitolo 5 Struttura del database CREATE TABLE tm_eventi ( evt_id NUMBER(10) NOT NULL, evt_utn_id NUMBER(10) DEFAULT NULL, evt_trf_codice VARCHAR(10) DEFAULT NULL, evt_dateopen DATE DEFAULT NULL, evt_dateclose DATE DEFAULT NULL, evt_allegato BLOB, evt_nomeallegato VARCHAR(25) DEFAULT NULL, evt_codice_esterno VARCHAR(10) DEFAULT NULL, evt_razza VARCHAR(25) DEFAULT NULL, evt_sesso VARCHAR(1) DEFAULT NULL, evt_eta NUMBER(3) DEFAULT NULL, evt_altezza NUMBER(8,4), evt_um_altezza VARCHAR(10), evt_peso NUMBER(8,4), evt_um_peso VARCHAR(10), evt_memo VARCHAR(255), PRIMARY KEY (evt_id), FOREIGN KEY (evt_trf_codice) REFERENCES TM_TIPOESAMI(tes_codice), FOREIGN KEY (evt_utn_id) REFERENCES TM_UTENTI(utn_id) ); CREATE SEQUENCE TM_EVENTI_SEQ START WITH 1 INCREMENT BY 1 NOMAXVALUE; CREATE TRIGGER TM_EVENTI_TRIGGER BEFORE INSERT ON TM_EVENTI FOR EACH ROW BEGIN SELECT TM_EVENTI_SEQ.NEXTVAL INTO :NEW.EVT_ID FROM DUAL; END; -----------------------------------------------------------------------------------------CREATE TABLE tm_referti ( ref_ind NUMBER(10) NOT NULL, ref_evt_id NUMBER(10) DEFAULT '0' NOT NULL, ref_utn_id NUMBER(10) DEFAULT '0' NOT NULL, ref_allegato_referto BLOB, ref_nomeallegato_referto VARCHAR(25), ref_datereferto DATE, PRIMARY KEY (ref_ind), FOREIGN KEY (ref_evt_id) REFERENCES TM_EVENTI (evt_id), FOREIGN KEY (ref_utn_id) REFERENCES TM_UTENTI (utn_id) ); CREATE SEQUENCE TM_REFERTI_SEQ START WITH 1 INCREMENT BY 1 NOMAXVALUE; 79 Capitolo 5 Struttura del database CREATE TRIGGER TM_REFERTI_TRIGGER BEFORE INSERT ON TM_REFERTI FOR EACH ROW BEGIN SELECT TM_REFERTI_SEQ.NEXTVAL INTO :NEW.REF_IND FROM DUAL; END; -----------------------------------------------------------------------------------------CREATE TABLE TM_PERMISSIONI ( per_utn_id NUMBER(10) DEFAULT '0' NOT NULL, per_tes_codice VARCHAR(10) DEFAULT '' NOT NULL, PRIMARY KEY (per_utn_id, per_tes_codice), FOREIGN KEY (per_utn_id) REFERENCES TM_UTENTI(utn_id), FOREIGN KEY (per_tes_codice) REFERENCES TM_TIPOESAMI (tes_codice) ); ------------------------------------------------------------------------------------------ Inserimenti per test INSERT INTO TM_UNITAEROGANTI (uer_codente, uer_codstruttura, uer_codspecialita, uer_codunitaerogante, uer_descr) VALUES ('EN_001','ST_001','01','U_001','PoliAmbulatorio 2000'); INSERT INTO TM_UTENTI(UTN_CN, UTN_TYPE, UTN_CODENTE, UTN_CODSTRUTTURA, UTN_CODSPECIALITA, UTN_CODUNITAEROGANTE, UTN_USERNAME, UTN_PASSWORD) VALUES('Prova2', 'R', 'EN_001','ST_001','01','U_001','stage02', 'stage02'); INSERT INTO TM_UTENTI(UTN_CN, UTN_TYPE, UTN_CODENTE, UTN_CODSTRUTTURA, UTN_CODSPECIALITA, UTN_CODUNITAEROGANTE, UTN_USERNAME, UTN_PASSWORD) VALUES('Prova', 'R', 'EN_001','ST_001','01','U_001','stage01', 'stage01'); INSERT INTO TM_TIPOESAMI(tes_codice, tes_descr) VALUES ('ES_0001','Elettrocardiogramma'); INSERT INTO TM_TIPOESAMI (tes_codice, tes_descr) VALUES ('ES_0002','Radiografia'); INSERT INTO TM_PERMISSIONI(PER_UTN_ID, PER_TES_CODICE) VALUES(2,'ES_0002'); INSERT INTO TM_PERMISSIONI(PER_UTN_ID, PER_TES_CODICE) VALUES(1,'ES_0001'); INSERT INTO TM_PERMISSIONI(PER_UTN_ID, PER_TES_CODICE) VALUES(1,'ES_0002'); Nota bene: su TM_EVENTI mettere dei default ai campi 80 Capitolo 5 Struttura del database ALTER TABLE TM_EVENTI MODIFY EVT_NOMEALLEGATO DEFAULT '--' ALTER TABLE TM_EVENTI MODIFY EVT_CODICE_ESTERNO DEFAULT '--' ALTER TABLE TM_EVENTI MODIFY EVT_RAZZA DEFAULT '--' ALTER TABLE TM_EVENTI MODIFY EVT_SESSO DEFAULT '-' ALTER TABLE TM_EVENTI MODIFY EVT_ETA DEFAULT 0 ALTER TABLE TM_EVENTI MODIFY EVT_MEMO DEFAULT '-' --- Aggiunto campo MEMO alla tabella dei referti --ALTER TABLE TM_REFERTI ADD REF_MEMO VARCHAR(255) ALTER TABLE TM_EVENTI MODIFY EVT_MEMO VARCHAR(255) --- Aggiunto campo REF_DEFINITIVO alla tabella dei referti che indica se un referto è definitivo (Y) o ancora modificabile (N) --ALTER TABLE TM_REFERTI ADD ref_definitivo CHAR(1); 81 6. Configurazione Server di sviluppo 6.1 Ambiente di sviluppo Come detto nel capitolo 3 nella descrizione della fase di deploy, “le macchine di sviluppo, soprattutto se si utilizzano ambienti di sviluppo molto sofisticati, sono configurate diversamente da quelle di test, pertanto il passaggio da un ambiente all'altro potrebbe non essere indolore”. Nel nostro caso il server di sviluppo è una semplicissima macchina con piattaforma Windows nella quale abbiamo istallato tutti gli strumenti per la realizzazione del Web service (tomcat, axis, eclipse) mentre la macchina di test, quella che effettivamente è il server e che permette la pubblicazione sul web, gira su un sistema operativo Linux. Linux è un sistema operativo di tipo Unix. La principale differenza rispetto ai “veri” sistemi Unix consiste nel fatto che Linux può essere copiato liberamente e gratuitamente compreso l’intero codice sorgente. Il termine Linux, per essere precisi, si riferisce solo al kernel. Il kernel è il nucleo essenziale del sistema operativo che si occupa della gestione della memoria, ei processi e del controllo hardware e che essenzialmente permette a tutti gli altri programmi di girare. La maggior parte delle persone, comunque, quando parlano di Linux, intendono un sistema operativo completo comprensivo delle applicazioni che ci girano sopra. Nella fase di installazione del sistema operativo per un server Tomcat le partizioni da creare sono le seguenti: o / o /boot o /home o /SWAP o /tmp o /usr o /usr1 o /var Nella ripartizione della memoria ad, usr1 deve essere assegnata la maggior parte della memoria, circa 2/3. La restante parte va suddivisa sugli altri F.S. 82 Capitolo 6 Configurazione Server di sviluppo 6.2 Installazione Java sul Server Scaricare Java nella versione j2sdk-1_4_2_07-rp.bin o copiare l’eseguibile da un altro server. Portare il file sotto /usr/src/ e cambiare i permessi del file in 777 con chmod 777 j2sdk-1_4_2_07-rp.bin. Lanciare l’eseguibile e seguire l’installazione guidata. Con l’installazione viene generato nella stessa dir un file j2sdk-1_4_2_07.rpm. Lanciare l’rpm con rpm-Uhv j2sdk-1_4_2_07.rpm. Java verra installato sotto /usr/java/j2sdk, quindi per uniformarci alle altre installazioni spostiamo l’installazione sotto /usr/local/java con l’istruzione mv /usr/java/j2sdk /usr/local/java. Andare sul server di test o produzione e copiare le librerie che si trovano sotto /usr/local/java/jre/lib/ext. Per copiarle utilizzare l’istruzione tar-czvf../libext.tar.gz*. Spostare il file tar.gz nella dir ext di java appena installato ed eseguire tar xzvf libext.tar.gz per compattare le librerie, senza sovrascrivere quelle esistenti. Cambiare i permessi alle librerie per l’utente creato (es.Tomcat) con l’istruzione chown tomcat:tomcat*. 6.3 Installazione TOMCAT su Server Creare un utente Tomcat con le seguenti istruzioni: groupadd –g 91 tomcat useradd –d /home/tomcat –g tomcat –u 91 tomcat Nella creazione del gruppo e dell’utente è importante specificare l’id del gruppo e dell’utente solo se il web server in questione fa parte di un cluster. In questo caso gli id devono essere identici su tutti i server. Per eseguire l’installazione bisogna scaricare tomcat nella versione jakarta- tomcat.4.1.31.gz sotto /usr/src. Lanciare jakarta-tomcat.4.1.31.gz con l’istruzione tar xvzf jakarta-tomcat.4.1.31.tar.gz. Viene generata una dir jakarta-tomcat.4.1.31 nella dir corrente. Per allineare il server agli altri, muovere il contenuto della dir sotto /home/tomcat. 83 Capitolo 6 Configurazione Server di sviluppo Entrare nella dir jakarta-tomcat.4.1.31 ed eseguire l’istruzione mv * /home/tomcat. Oppure muovere l’intera dir sotto /home/tomcat con l’istruzione mv jakartatomcat.45.1.31 /home/tomcat. Dal server di produzione o sviluppo copiare le librerie che si trovano sotto la home-dir di tomcat sotto /common/lib. Comprimerle con l’istruzione tar –czvf ../tomcatlib.tar.gz*. Il file compresso verrà generato nella dir padre. Muovere il file nella dir /common/lib di tomcat appena installato e decomprimerlo con l’istruzione tar xvzf tomcatlib.tar.gz senza sovrascrivere le librerie esistenti. Cambiare i permessi a tutti i file all’interno della della home-dir di tomcat per l’utente tomcat con l’istruzione chown tomcat:tomcat –R*. Modificar i file : - catalina.sh - tomcat-user.xml - .bash_profile - server.xml # solo se deve essere effettuato un reindirizzamento Nel file catalina.sh dopo la prima parte inserire quanto segue modificando opportunamente le variabili d’ambiente: JAVA_OPTS=”-server –Xms256M –Xmx750M –XX:MaxPermSize=size” JAVA_HOME=/usr/local/java; export JAVA_HOME CATALINA_HOME=/usr1/Jakarta-tomcat-5.5.4; export CATALINA_HOME CATALINA_OPTS=”-Djava.awt.headless=true –Djava.net.preferIPv4Stack=true”; export CATALINA_OPTS CATALINA_OPTS=”-Djava.awt.headless=true”; export CATALINA_OPTS PATH=$PATH:$JAVA_HOME/bin; export PATH Nel file tomcat-user.xml eliminare tutto e copiare quanto segue modificando gli utenti e le password se necessario: <?xml version=’1.0’ encoding=’utf-8’?> <tomcat-users> <role rolename=”tomcat”/> <role rolename=”manager”/> <role rolename=”admin”/> <role rolename=”role1”/> <user username=”admin” password=”admin” roles=”admin, manager”/> <user username=”tomcat” password=”tomcat” roles=”tomcat”/> <user username=”both” password=”tomcat” roles=”tomcat, role1”/> <user username=”role1” password=”tomcat” roles=”role1”/> 84 Capitolo 6 Configurazione Server di sviluppo </tomcat-users> Nel file .bash_profile eliminare tutto ed inserire quanto segue: if [ -f ~/.bashrc ]; then .~/.bashrc fi export JAVA_HOME=/usr/local/java PATH=$PATH:$HOME/bin:$JAVA_HOME/bin; export PATH export CATALINA_HOME=/home/tomcat4 unset USERNAME Dal server di produzione o di test copiare il file tomcat che si trova sotto /etc/rc.d/init.d e riportarlo nel server interessato nella stessa posizione. Dopo averlo importato modificare i permessi del del file rendendoli equivalenti agli altri. Lanciare Tomcat lanciando il file tomcat con ./tomcat start e fermarlo con ./tomcat stop 6.4 Installazione Oracle Database 10g XE Oracle Database 10g XE si basa sul codice di Oracle Database 10g Release 2, consente a chiunque di provare a costo zero tutte le funzionalità presenti in Oracle Database 10g. . La versione light di Oracle offre infatti gli stessi strumenti del fratello maggiore, dando la possibilità agli utenti di sviluppare applicazioni in ambiente Java, .NET e PHP. Oracle XE utilizza al massimo 1 GB di memoria, gestisce una base di dati con una dimensione massima di 4 GB e permette l'esecuzione di una sola istanza per sistema, gira sui sistemi operativi Windows e su una gran varietà di distribuzioni Linux. Con il suo nuovo database gratuito, rilasciato negli scorsi giorni in versione beta, Oracle intende rivolgersi in modo particolare agli sviluppatori, alle piccole e medie aziende e agli studenti. Della prima categoria il colosso corteggia i developer open source e, più in generale, coloro che utilizzano tecnologie come PHP, Java e MS.NET. 85 Capitolo 6 Configurazione Server di sviluppo La licenza che accompagna Oracle Database XE blinda il codice sorgente del software, tuttavia consente agli sviluppatori di ridistribuire il programma e di inglobarlo all'interno delle proprie applicazioni senza pagare alcuna royalty. I programmatori possono inoltre sfruttare la piena compatibilità di XE con i suoi fratelli maggiori: ogni applicazione sviluppata per l'edizione Express gira infatti, senza alcuna modifica, anche sulle edizioni Standard ed Enterprise di Oracle database 10g. Alcuni sostengono che con questa mossa Oracle intenda contrastare l'avanzata, nel mercato delle piccole e medie aziende, di software open source come MySQL e PostgreSQL. C'è tuttavia da sottolineare come Oracle Database contenga diverse limitazioni tecniche, tra cui il supporto ad un solo processore e ad un massimo di 1 GB di memoria RAM e di 4 GB di dati utente. Guarda caso queste sono le stesse restrizioni che caratterizzeranno l'imminente SQL Server Express di Microsoft, a cui Oracle Database 10g XE va chiaramente a pestare i piedi. Installazione Installazione di un web Server e di un db Server utilizzando: 1. GNU/Linux; 2. il web server Apache; 3. Oracle in versione 10g Express Edition, rilasciata per il pinguino. Requisiti di sistema e limitazioni di Oracle Database XE: Oracle Database XE presenta una serie di limitazioni relativamente all’hardware su cui il sistema viene installato e, in particolare: 1. se Oracle Database XE è installato su un computer con più di una CPU (incluse anche le CPU dual core), esso utilizzerà esclusivamente le risorse di una sola CPU; 2. su un singolo computer può essere effettuata l’installazione di una sola copia di Oracle Database XE; in aggiunta, gli utenti possono far girare una sola istanza del database Oracle su ciascun computer. Quest’ultima limitazione può comunque essere aggirata in quanto, anche sulla stessa installazione di Oracle Database XE possono convivere più schemi, ciascuno dei quali contenente le proprie tabelle. Di 86 Capitolo 6 Configurazione Server di sviluppo fatto, è come se sulla stessa macchina fossero presenti più database simultaneamente; 3. la quantità massima di dati contenuta in un database Oracle 10g XE non può superare i 4 gigabytes di spazio disco, tuttavia se i dati degli utenti dovessero superare tale quota, il sistema invia l’errore ORA-12592; 4. la quantità massima di memoria RAM che il server Oracle Database XE utilizza non può eccedere un gigabytes, anche se ne è disponibile una quantità maggiore sul sistema usato come server. La quantità di memoria totale utilizzata dal server Oracle viene ricavata come somma della System Global Area (SGA) e della Program Global Area (PGA) aggregata; 5. il protocollo HTTPS (Secure HTTP) non è supportato nativamente dal listener HTTP presente in Oracle XE, quindi è necessario far uso di un web server, come Apache, che supporti tale protocollo. Spazio di swap richiesto per Oracle Database XE Server: Memoria RAM presente sul server Tra 0 e 256 MB Tra 256 e 512 MB 512 MB e oltre Spazio di swap richiesto 3 volte la dimensione della RAM 2 volte la dimensione della RAM 1024 MB di RAM Installazione e configurazione di Oracle Database XE Server: La procedura di installazione e configurazione della componente server di Oracle Database XE si suddivide in tre passaggi fondamentali. 1. Impostazione dei parametri del kernel: E’ necessario settare alcuni parametri del kernel per un funzionamento ottimale di Oracle. Nella tabella seguente riporto i parametri necessari ed il corrispondente valore che essi dovrebbero assumere: 87 Capitolo 6 Parametro del kernel sem semmns semopn semmni shmmax shmmni shmall file-max ip_local_port_range rmem_default wmem_default rmem_max wmem_max Configurazione Server di sviluppo Valore 250 32000 100 128 536870912 4096 2097152 65536 1024-65000 262144 262144 262144 262144 Per prima cosa diamo un’occhiata per verificare che il valore impostato di default sulla nostro distribuzione per questi parametri del kernel sia coerente con quanto indicato nella tabella precedente, tramite il comando sysctl. # /sbin/sysctl -a | grep shm kernel.shmmni = 4096 kernel.shmall = 2097152 kernel.shmmax = 536870912 kernel.shm-use-bigpages = 0 # /sbin/sysctl -a | grep sem kernel.sem = 250 32000 100 128 # /sbin/sysctl -a | grep file-max fs.file-max = 65536 # /sbin/sysctl -a | grep ip_local_port_range net.ipv4.ip_local_port_range = 1024 65000 # /sbin/sysctl -a | grep rmem_default net.core.rmem_default = 262144 # /sbin/sysctl -a | grep rmem_max net.core.rmem_max = 262144 # /sbin/sysctl -a | grep wmem_default net.core.wmem_default = 262144 # /sbin/sysctl -a | grep wmem_max net.core.wmem_max = 262144 Se tali parametri non sono corretti è necessario editare il sysctl.conf, oppure usare il seguente comando: 88 Capitolo 6 Configurazione Server di sviluppo # cat >> /etc/sysctl.conf <<EOF kernel.sem = 250 32000 100 128 kernel.shmmax = 536870912 kernel.shmmni = 4096 kernel.shmall = 2097152 fs.file-max = 65536 net.ipv4.ip_local_port_range = 1024 65000 net.core.rmem_default=262144 net.core.wmem_default=262144 net.core.rmem_max=262144 net.core.wmem_max=262144 EOF 2. Installazione e configurazione di Oracle Database XE: Accettato il contratto di licenza, è necessario installare prima di tutto la libreria libaio, come indicato precedentemente (la libreria glibc, infatti, è presente di default in questa distribuzione), quindi potete procedere all’installazione dell’rpm di Oracle Database Server XE come utente root: # rpm –ivh oracle-xe-[versione].rpm Per chi usa Debian, o Ubuntu, invece, una volta scaricato il deb corrispondente sempre dalla pagina web indicata, il comando da lanciare è il classico dpkg: # dpkg –i oracle-xe-[versione].deb in questo caso non è necessario l’installazione di nessuna libreria aggiuntiva. Bene, ora è giunto il momento di passare alla configurazione del nostro Oracle Database Server. Nella directory /etc/init.d/ si trova uno script di configurazione di Oracle chiamato oracle-xe, da lanciare con l’opzione configure: # ./oracle-xe configure • Lo script vi richiede una serie di informazioni per la configurazione del database Oracle tra cui: il numero di porta (per default la 8080) tramite cui è possibile raggiungere l’interfaccia web per la configurazione di Oracle all’url: http://[hostname o indirizzo IP]:8080/htmldb • il numero di porta (per default la 1521) su cui si mette in attesa il “listener” di Oracle Server, ossia un demone preposto a ricevere e servire le richieste di connessione al server Oracle, appunto. Se il vostro firewall è configurato in modo 89 Capitolo 6 Configurazione Server di sviluppo tale da filtrare la porta di default per il listener di Oracle, specificate un valore a voi più congeniale! • quando far partire l’istanza di Oracle Database Server; si può decidere se schedulare la partenza di Oracle al boot del sistema (e questo implica anche la partenza immediata del DBMS), oppure se eseguirla manualmente di volta in volta. Per verificare se il DB è attivo bisogna controllare tramite il comando: # ./oracle-xe status LSNRCTL for Linux: Version 10.2.0.1.0 - Beta on 14-FEB-2007 10:14:01 Copyright (c) 1991, 2005, Oracle. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))) <— EVVAI IL LISTENER E’ ATTIVO!!! STATUS of the LISTENER ———————— Alias LISTENER Version TNSLSNR for Linux: Version 10.2.0.1.0 - Beta Start Date 14-FEB-2007 10:13:44 Uptime 0 days 0 hr. 0 min. 17 sec Trace Level off Security ON: Local OS Authentication SNMP OFF Default Service XE Listener Parameter File /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/network/admin/listener.ora Listener Log File /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/network/log/listener.log Listening Endpoints Summary… (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))) Services Summary… Service “PLSExtProc” has 1 instance(s). Instance “PLSExtProc”, status UNKNOWN, has 1 handler(s) for this service… The command completed successfully 3. Impostazione delle variabili d’ambiente: Un ultimo passo infine riguarda la configurazione delle variabili d’ambiente, senza cui non è possibile accedere nemmeno al prompt di SQL*Plus per le classiche operazioni di 90 Capitolo 6 Configurazione Server di sviluppo gestione del database come inserimenti, modifiche, cancellazioni e consultazioni di dati e così via. Per procedere in questo senso, è sufficiente lanciare uno script di configurazione delle variabili d’ambiente di Oracle Database XE, disponibile in due distinte versioni a seconda della shell che si utilizza. Lo script in questione, che si trova nella directory: /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/bin/ è oracle_env.sh (valido anche per le shell Bourne e Korn, mentre per C e tcsh shell, lo script da usare è oracle_env.csh, presente sempre nella medesima directory). Lo script oracle_env.sh, si occupa di: 1. impostare la variabile ORACLE_HOME con il path dell’installazione di Oracle Database XE; 2. impostare la variabile ORACLE_SID che conterrà il nome del database; 3. impostare la variabile NLS_LANG con il valore ritornato dallo script nls_lang.sh; quest’ultimo si occupa di determinare il character set predefinito con cui i dati verranno inseriti nel database e visualizzati tramite un’applicazione client come, ad esempio SQL*Plus. Il parametro NLS_LANG utilizza il seguente formato: NLS_LANG = LANGUAGE_TERRITORY.CHARACTER_SET LANGUAGE specifica il linguaggio con cui vengono visualizzati i messaggi di Oracle ed i nomi dei giorni e dei mesi; TERRITORY specifica le convenzioni culturali per date, numeri, tempo e formati monetari, mentre il valore CHARACTER_SET indica la lingua da utilizzare per l’inserimento e la visualizzazione dei dati nel database di Oracle. 4. impostare la variabile PATH con il percorso che porta alla directory contenente gli eseguibili di Oracle database XE; 5. impostare la variabile LD_LIBRARY_PATH con il percorso della directory contenente le librerie principali per Oracle XE. Va ricordato che riavviando il computer, i settaggi impostati con lo script oracle_env.sh vengono persi, perciò è buona norma inserire la stringa: 91 Capitolo 6 Configurazione Server di sviluppo /usr/lib/oracle/xe/app/oracle/product/10.2.0/server/bin/oracle_env.sh all’interno del file .bash_profile; inoltre, per poter mantenere le stesse impostazioni all’apertura di ogni nuova shell durante la sessione corrente, è conveniente aggiungerlo anche all’interno del file .bashrc. 6.5 Installazione e configurazione di Apache Una volta che il db server Oracle è installato e configurato, si può procedere con l’installazione del web server Apache tramite i pacchetti forniti dalla vostra distro oppure da sorgenti. Questi sono i comandi che bisogna lanciare per il download, lo scompattamento e l’installazione di Apache sul mio server: # wget -c http://apache.fis.uniroma2.it/httpd/httpd-2.2.3.tar.gz # tar xzf httpd-2.2.3.tar.gz /usr/local/src/ # ./configure --prefix=/usr/local/apache2/ # make && make install il parametro –prefix passato al comando configure richiede il path in cui verrà installato il web server Apache. Dalla documentazione del web server si legge che il percorso di default è /usr/local/apache2/ e io ho usato proprio questo. Terminata la compilazione di Apache, è necessario modificare opportunamente il file di configurazione httpd.conf (per chi ha compilato da sorgenti senza specificare un path di installazione diverso da quello di default proposto nella documentazione di Apache, questo file è posizionato in /usr/local/apache2/conf/). 92 Capitolo 6 Configurazione Server di sviluppo Figura – Istallazione di Apache 93 7. Problematiche e miglioramenti 7.1 Il Server Linux Nonostante siano state prese tutte le dovute precauzioni in fase di programmazione, soprattutto nella gestione delle path di file e di directory, qualche piccolo, ma fondamentale aspetto rendeva le due fasi (di sviluppo e di test) non completamente dipendenti e perciò nel passaggio alla fase di test ogni volta bisogna avere l’accortezza e la pazienza di tornare sul codice. Vediamo brevemente di cosa si tratta. PRECAUZIONI: La disponibilità di implementazioni della JVM (Java Virtual Machine) per diversi ambienti operativi è la chiave della portabilità di Java, proclamata nello slogan write once, run everywhere ("scrivi una volta, esegui dappertutto"). La macchina virtuale realizza infatti un ambiente di esecuzione omogeneo, che nasconde al software Java (e quindi al programmatore) qualsiasi specificità del sistema operativo sottostante. Ciò ha permesso di non dover andare a modificare metodi e funzioni specifiche e soprattutto di poter definire univocamente una delle questioni più delicate nella gestione di files: le path. La grande maggioranza dei servizi implementati si basa sui due seguenti passi: • Creazione di un file .xml tramite una query SQL; • Invio del file appena creato; Il file xml viene salvato fisicamente sul file system nella cartella temp dell’Application Server (il tomcat). Onde evitare incompatibilità tra i vari sistemi operativi definiamo tale percorso come: Path=System.getProperty("catalina.home")+File.separator+"temp"+File.separ ator; 94 Capitolo 7 Problematiche e miglioramenti Che corrisponde: WINDOWS C:/TOMCAT554/temp/ LINUX /var/jakarta-tomcat-5.5.4/temp/ System.getProperty("catalina.home") restituisce il valore della variabile di ambiente CATALINA HOME impostata in fase di istallazione dell’Apllication Server mentre il File.separator inserisce il carattere di separazione della path assoluta di un file (slash per Linux, back-slash per windows). Questo è stato un semplice esempio di portabilità di java in quanto siamo riusciti a passare da windows a linux senza dover effettuare nessun cambiamento. Purtroppo però le cose non vanno sempre così, infatti in fase di attachment: //Invio del file xml ottenuto dalla query come allegato message=(org.apache.axis.MessageContext.getCurrentContext()).getResponseM essage(); /*VERSIONE PER LINUX*/ message.addAttachmentPart(message.createAttachmentPart(new DataHandler(new URL("file:"+File.separator+File.separator+fileNamePath+fileName)))); /*VERSIONE PER WINDOWS*/ message.addAttachmentPart(message.createAttachmentPart(new DataHandler(new URL("file:"+File.separator+fileNamePath+fileName)))); Come si può notare dal frammento di codice i due sistemi operativi si comportano in modo differente nella gestione dell’oggetto URL. La sintassi in questione è: file: // Protocollo FilePath+FileName Doppio File.separator Nome del file L’incongruenza tra i due sistemi sta nel fatto che windows accetta una sintassi con un solo file separator, mentre linux una sintassi con due file separator. Risultato di ciò è che se non si accetta questa predisposizione si ha un errore in compilazione. 95 Capitolo 7 Problematiche e miglioramenti Un altro aspetto di fondamentale importanza sempre riguardo all’utilizzo di due sistemi operativi differenti è la gestione degli attachments. Dopo un lungo studio e una lunga fase di test abbiamo imparato che quando la dimensione dell’allegato al messaggio SOAP supera una certa soglia il sistema operativo si fa carico di prendere tale allegato e salvarlo temporaneamente sotto una sua ben specificata directory per poi poterlo inviare al destinatario. Il percorso di questa directory è specificato in un file di configurazione, serverconfig.wsdd che viene creato, lato server, automaticamente dall’eclipse in fase di generazione del Web service. Leggendo le righe di codice di questo file, ad un certo punto si incontra il seguente frammento: <globalConfiguration> <parameter name="sendMultiRefs" value="true"/> <parameter name="disablePrettyXML" value="true"/> <parameter name="adminPassword" value="admin"/> <parameter name="attachments.Directory" value="C:\workspace\.metadata\.plugins\org.eclipse.wst.s erver.core\tmp0\webapps\jtelemed\WEBINF\attachments"/> <parameter name="dotNetSoapEncFix" value="true"/> Facendo un deploy dell’intero progetto come war sul server linux anche il file serverconfig.wsdd viene importato con tutte le sue caratteristiche e soprattutto con la sua attachments.Directory impostata con una path valida sotto windows ma non sotto linux: occorre definire una nuova path: <globalConfiguration> <parameter name="sendMultiRefs" value="true"/> <parameter name="disablePrettyXML" value="true"/> <parameter name="adminPassword" value="admin"/> <parameter name="attachments.Directory" value="/var/jakarta-tomcat5.5.4/webapps/jtelemed_server/WEBINF/attachment"/> <parameter name="dotNetSoapEncFix" value="true"/> 96 Capitolo 7 7.2 Problematiche e miglioramenti Interoperabilità ed evoluzioni future I Web Services sono nati soprattutto per superare i problemi di interoperabilità tra le normali architetture a componenti. Questo scopo non è ancora stato completamente raggiunto a causa della giovinezza degli standard, dalla mancanza di standardizzazione e dell'assenza di implementazioni di riferimento. Attualmente l'XML Protocol Working Group è ancora al lavoro, e ha prodotto il Working Draft delle specifiche SOAP 1.2. Esistono vari tipi di problemi che possono pregiudicare la comunicazione tra le varie implementazioni, ma sono raggruppabili in problemi che riguardano il file WSDL, il protocollo SOAP, e il protocollo di trasporto. Il modello dei Web Services è ancora molto giovane, quindi molti punti importanti presenti nelle architetture più diffuse sono ancora in attesa di una definizione e/o standardizzazione. Ad esempio la gestione della sicurezza, dell'autenticazione, delle transazioni, dell'interazione fra più componenti, sono ancora tutte da definire e standardizzare. Esistono alcuni protocolli che sono stati proposti per risolvere questi problemi: • • • • • XML Signature e SOAP-DS (SOAP Digital Signature) - Sono protocolli per la gestione di messaggi XML e SOAP con firma elettronica, per garantire l'integrità dei messaggi e la loro provenienza SOAP with Attachments - È un protocollo per la gestione degli allegati per messaggi SOAP XAML - È un protocollo basato su XML per la gestione di transazioni utilizzanti lo schema Two-Phase Commit WSFL e XLANG - Sono protocolli per la gestione del workflow di componenti e la gestioni di transazioni utilizzanti il metodo della compensazione XML Encryption - È un protocollo per la cifratura di documenti XML. Nessuno di questi protocolli è ancora uno standard affermato, sono solo proposte più o meno supportate. XML Signature e XML Encryption sono in corso di approvazione da parte del W3C. 97 Capitolo 7 7.3 Problematiche e miglioramenti Possibili sviluppi futuri Un ultima considerazione va fatta in considerazione dei possibili sviluppi futuri riguardano l’uso di ambienti di sviluppo alternativi a Site Painter e Portal Studio che si sono rilevati molto efficienti ma che comunque non sono freeware. Quindi l’attenzione in questo paragrafo si sposterà sul trovare degli IDE (Integrated development environment) di sviluppo alternativi al Site Painter e al Portal Studio della Zucchetti, ma ugualmente efficienti. Jdeveloper 5.0 Jdeveloper 5.0 si rivela uno strumento originale, e un'ottima piattaforma di sviluppo in Java per creare Web service, ed inoltre è completamente gratuito. Jdeveloper non serve solo per scrivere in codice Java. È un vero e proprio trampolino per il mondo dei servizi Web. Infatti Oracle sostiene che questo componente della sua piattaforma Oracle9i rappresenta una importante unificazione delle tecnologie chiave per lo sviluppo di applicativi per Internet: Java, XML e SQL. Oracle9i offre il supporto all'UML (Unified Modeling Language), allo sviluppo di tipo Rapid Application Development attraverso wizard e alla costruzione di report, query e analisi per la business intelligence integrata. Oracle sostiene che la nuova versione di Oracle 9i Jdeveloper aiuterà gli sviluppatori a scrivere codice Java corretto e ottimizzato e che, grazie al supporto di standard come SOAP, UDDI e WSDL, garantirà interoperabilità fra servizi e massima integrazione con le applicazioni preesistenti. In sintesi Jdeveloper 5, l’ambiente Java gratuito di Oracle, si rivela un tool set elegante e completo, in grado di competere con concorrenti che, invece, costano parecchio, anche nel nuovo campo dello sviluppo di servizi Web. Molto configurabile, lavora su Linux e Solaris, oltre che Windows Nt, e supporta tutte le più recenti tecnologie Xml, come Wsdl. Pro - Possibilità di personalizzare la vista delle finestre; tutorial; ambiente completo ed elegante 98 Capitolo 7 Problematiche e miglioramenti Contro - Il tool di diagnostica richiede l’intervento manuale dello sviluppatore; piccoli malfunzionamenti su Windows 2000. IDE di Kylix Un’applicazione con Kylix viene creata raggruppando form, codice sorgente e risorse in un progetto: una collezione di file generata dal programmatore e dall’ambiente al fine di ottenere le specifiche dei moduli da compilare e per poter eseguire una compilazione incrementale dei soli moduli effettivamente modificati. Tutte le distribuzioni di Kylix 2 supportano la programmazione di un’ampia gamma di applicazioni grafiche, oggetti condivisi, package ed altri programmi. La novità maggiore consiste nella possibilità di utilizzare componenti per lo sviluppo di Web Service: BizSnap, WebSnap, DataSnap. BizSnap, estende le funzionalità di Linux e Apache con il supporto per XML e tecnologie WSDL e SOAP. La piattaforma di sviluppo BizSnap semplifica l’integrazione business-tobusiness mediante l’utilizzo di XML/SOAP per connettere in modo trasparente applicazioni WEB. Kylix, inoltre, permette di scambiare, trasformare e manipolare documenti XML e creare applicazioni che possano comunicare direttamente con quelle dei partner, con piattaforme abilitate ai Web Service come .Net e BizTalk di Microsoft e Sun ONE di Sun Microsystems. WebSnap rende Apache un potente server di classe enterprise per applicazioni Web database-driven. DataSnap supporta i più diffusi database server grazie ai driver nativi per Oracle, DB2, Informix, PostreSQL, MySQL e Borland InterBase e si basa sull’elevata produttività di Kylix 2 per creare applicazioni scalabili all’aumentare del volume di transazioni e del numero di utenti. DataSnap, basato su tecnologie standard quali SOAP, serve ad integrare applicazioni esistenti in primo luogo quelle per e-commerce. All’avvio di Kylix appare l’insieme degli strumenti dell’IDE: toolbar, menu, palette delle componenti, editor per il codice, code explorer ed il project manager. La finestra 99 Capitolo 7 Problematiche e miglioramenti principale, che occupa la parte alta dello schermo, contiene il menu, le toolbar (per un accesso immediato alle funzioni del menu più utilizzate) e la palette dei componenti. Un altro utile tool messo a disposizione dall’IDE di Kylix è il repository degli oggetti (Object Repository) che contiene componenti, con caratteristiche generali, pronti per essere utilizzati o ereditati, nei progetti. La caratteristica fondamentale di Kylix (ed in generale degli strumenti RAD) è la metodologia con cui, nei progetti, si costruiscono le interfacce utente. Uno strumento RAD permette di comporre, nel vero senso della parola, una applicazione a partire da "mattoncini" software predefiniti; quindi è possibile trovare componenti visuali come bottoni, edit box oppure label; e componenti non visuali quali: data module, timer oppure socket. 100 8. Pubblicazione 8.1 Il Firewall di Linux Un Firewall linux non è altro che un semplice personal computer normalmente collegato fisicamente (con dei cavi) a più di una rete. Un firewall avrà quindi installata una scheda di rete (interfaccia) per ogni rete cui sarà collegato. Inoltre, dovrà costituire l'unico punto di contatto tra le reti cui è connesso, ovvero dovrà costituire l'unico punto di collegamento tra due o più reti. Un po' come un ponte che collega due o più isole. Senza entrare nei dettagli del protocollo ip, tutte le comunicazioni tra queste reti saranno quindi costrette a passare da questo computer. Questo computer, una volta configurato come firewall, sarà in grado di decidere cosa far passare, cosa scartare o come reagire a determinati eventi. Tornando all'esempio del nostro ponte, configurare il computer come firewall può essere considerato equivalente a mettere una stanga e 2 finanzieri che controllano tutti i veicoli e le persone in transito. Questi 2 finanzieri saranno quindi in grado di agire secondo le istruzioni che gli verranno date dal ministero, bloccando veicoli, arrestando i contrabbandieri o sparando a vista ai ricercati. In linux, la stanga e i finanzieri sono messi a disposizione direttamente dal kernel. Per controllare il kernel, per dare cioè le direttive ai finanzieri, vengono messi a disposizione diversi comandi: con il kernel 2.4, i principali e più avanzati sono comunque il comando ip, tc e iptables, i primi due inclusi nel pacchetto iproute mentre il terzo distribuito indipendentemente. Procedendo con ordine, in linea di principio il comando ip consente di creare nuove isole e nuovi ponti, di gestire quindi le tabelle di routing e le interfacce di rete, il comando iptables di istruire i finanzieri dicendo cosa far passare e cosa non far passare o come reagire a determinate condizioni, consente cioè di creare dei filtri. Infine, il comando tc, consente di stabilire delle priorità e dei limiti sul traffico in ingresso ed in uscita. Ad esempio, un compito che esula dal filtraggio e che quindi riguarda tc potrebbe essere quello di riservare alcune corsie al passaggio di veicoli di emergenza o di servizio, limitando magari il traffico automobilistico, di riservare cioè determinate quantità di banda a determinate tipologie di traffico. 101 Capitolo 8 Pubblicazione Riassumendo, quindi, si parlerà del comando: • ip per configurare, attivare, disattivare o creare interfacce di rete, nonché per gestire le tabelle di routing (da solo rimpiazza i comandi ifconfig, route e arp). • iptables per bloccare, consentire o registrare il traffico, per effettuare cioè firewalling, ovvero per modificare alcune caratteristiche dei pacchetti in transito, per effettuare cioè NAT (Network Address Translation). • tc per assegnare delle priorità, imporre delle precedenze o dei limiti di banda (tc sta per traffic classifier.), in poche parole, per effettuare dello shaping. Quello che più ci interessa è il comando iptables. 8.2 Il comando iptables Dovendo parlare di iptables, è indispensabile una piccola introduzione. Il parente più stretto di iptables è sicuramente ipchains il quale aveva introdotto il concetto di catena. iptables ha ripreso il concetto di catena per estenderlo tramite l'introduzione di tabelle. Prima di parlare di tabelle però, ci conviene chiarire il concetto di catena. L'idea di base è abbastanza semplice: un computer collegato in rete con una o più interfacce ha a che vedere con tre tipi di pacchetti: • Pacchetti destinati al firewall stesso • Pacchetti che devono transitare dal firewall ma destinati a qualcun altro • Pacchetti originati dal firewall stesso Di default ipchains ed iptables mettono quindi a disposizioni rispettivamente 3 catene: • La catena di input • La catena di forward • La catena di output In un computer normale (o in un server) verrebbero principalmente utilizzate le catene di input e di output per filtrare il traffico ricevuto e generato dalla macchina presa in considerazione. 102 Capitolo 8 Pubblicazione In un firewall invece, i pacchetti transiterebbero da una scheda di rete all'altra, passando quindi attraverso la catena di forward. Ogni catena è poi costituita da un insieme di regole. Ad ogni regola, è associata un'azione (target). Ogni volta che un pacchetto transita per una catena, tutte le regole vengono controllate in ordine, dalla prima inserita fino all'ultima. Quando una regola risulta applicabile al pacchetto (viene trovato un match) viene applicata al pacchetto l'azione specificata e viene interrotto il processo di ricerca. Se non viene trovata una regola applicabile al pacchetto, la sua sorte (l'azione d'applicare) viene scelta in base alla politica (policy) della catena stessa. In pratica, la policy indica cosa fare di tutti i pacchetti che non rientrano in nessuna regola. Sia ipchains che iptables poi, consentono di creare o rimuovere catene e di specificare come azione quella di percorrere un'altra catena. Definizione delle policy Iniziamo quindi a parlare di iptables, ed iniziamo proprio definendo la politica di default delle catene di base, cioè l'azione che deve essere presa nel caso in cui nessuna regola si possa applicare al particolare pacchetto. Per fare questo, basta dare: iptables -P INPUT DROP iptables -P OUTPUT DROP iptables -P FORWARD DROP Qui vediamo proprio che se un match non viene trovato, i pacchetti devono essere buttati via (DROP), proprio come se non fossero mai stati ricevuti (o richiesto l'invio, a secondo della catena). Oltre a DROP, esistono molte altre azioni che è possibile specificare, dove le principali sono: • ACCEPT , lascia passare il pacchetto • QUEUE, passa il pacchetto ad un programma in userspace Spesso però capita di usare delle target extensions, ossia dei particolari target che sono forniti tramite dei moduli esterni. E' buona abitudine mettere come politica di default quella di buttare 103 Capitolo 8 Pubblicazione via i pacchetti, dopodiché consentire esplicitamente tutto ciò che vogliamo fare passare. Questo principalmente per due motivi: • è facile rendersi conto di cosa si è dimenticato chiuso (molti degli utenti telefoneranno fino alla saturazione delle linee telefoniche pur di vedere il loro client preferito funzionare), mentre è molto più difficile vedere cosa si è dimenticato aperto. • non è facile riconoscere tutto ciò che c'è da bloccare. Per esempio, usando una politica di ACCEPT, è estremamente difficile prevedere ciò che potrà essere utilizzato per aggirare le restrizioni, contando che spesso non è obbligatorio seguire gli standard e una persona un po’ sveglia potrebbe modificare il kernel di due macchine linux per utilizzare un protocollo che nulla ha a che vedere col tcp e che mai noi ci saremmo immaginati di dover bloccare (trattandosi, magari, di un protocollo inventato di sana pianta). Un comando che può essere utile nello scrivere le regole si chiama nstreams: questo stamperà a video l'elenco di tutte le connessioni in corso su una rete. Modifiche on the fly - iptables da riga di comando Sebbene sia veramente poco piacevole, è possibile utilizzare iptables direttamente da riga di comando. Nonostante questo, alcune di queste funzioni possono risultare molto utili in fase di testing. Ad esempio, dovendo fare delle misure di banda può essere utile consentire temporaneamente solo il traffico tra determinate macchine, e non è il caso di perdere tempo a modificare file di configurazione o script dalla provata affidabilità. Iniziamo quindi col vedere le regole attive sul kernel, col comando iptables -nL dove n dice di non risolvere i nomi (passando da indirizzi ip a nomi di domini) e L di listare o elencare (in italiano) tutte le regole. Vediamo alcuni esempi: [root@jtelemed ~]# iptables –nL Chain INPUT (policy DROP) target prot opt source destination 104 Capitolo 8 ACCEPT ACCEPT ACCEPT REJECT Pubblicazione all tcp all tcp ----- 0.0.0.0/0 0.0.0.0/0 0.0.0.0/0 0.0.0.0/0 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 0.0.0.0/0 state RELATED,ESTABLISHED 0.0.0.0/0 reject-with tcp-reset Chain FORWARD (policy DROP) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination -n viene usato principalmente perché altrimenti ci vorrebbe molto tempo a generare l'output (tentando di connettersi al database). Un problema di iptables -nL (rispetto ad usare un file di configurazione) è che molti dettagli non vengono mostrati e comunque la leggibilità è molto minore. Per avere più dettagli, comunque, è possibile utilizzare l'opzione -v o -vv, con un comando simile a iptables –v -nL [root@jtelemed ~]# iptables –L -v Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source 1062 124K RH-Firewall-1-INPUT all -- any any anywhere destination anywhere Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source 0 0 RH-Firewall-1-INPUT all -- any any anywhere destination anywhere Chain OUTPUT (policy ACCEPT 1337 packets, 1311K bytes) pkts bytes target prot opt in out source destination Chain RH-Firewall-1-INPUT (2 references) pkts bytes target prot opt in 37 6707 ACCEPT all -- lo 43 2630 ACCEPT icmp -- any 0 0 ACCEPT ipv6-crypt -- any 0 0 ACCEPT ipv6-auth -- any 0 0 ACCEPT udp -- any 0 0 ACCEPT udp -- any 880 100 ACCEPT all -- any ESTABLISHED 0 0 dpt:http 0 0 out any any any any any any any source anywhere anywhere anywhere anywhere anywhere anywhere anywhere destination anywhere anywhere icmp any anywhere anywhere 224.0.0.251 udp dpt:5353 anywhere udp dpt :ipp anywhere state RELATED, ACCEPT tcp -- any any anywhere anywhere state NEW tcp ACCEPT tcp -- any any anywhere anywhere state NEW tcp -- any any anywhere anywhere state NEW tcp -- any any anywhere anywhere rejec with icmp- dpt :https 3 144 ACCEPT tcp dpt:8085 99 13912 REJECT all hosts-proibited 105 Capitolo 8 Pubblicazione Se si è interessati ad un'unica catena, si può specificarne il nome dopo il -nL. Con qualcosa di simile a iptables –vv -nL INPUT si vedrà solo la catena di INPUT ed un dump delle informazioni del kernel. Come altre opzioni generiche, si possono utilizzare -x, che mostrerà i valori esatti dei contatori, mentre -Z li azzererà, oppure -t nometabella per visualizzare altre tabelle. 106