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