Progetto di Laboratorio di Reti: chat-0.1

annuncio pubblicitario
Progetto di Laboratorio di Reti: chat-0.1
Federico Fortini
13 aprile 2006
1
Descrizione dei requisiti
Sviluppare un servizio di messaggistica (chat) su connessione cifrata con SSL.
La chat deve avere una architettura p2p, ovvero il programma utente contiene
sia la parte client che la parte server. Ogni utente possiede coppia key/cert
che usa per il server quando si mette in ascolto e per il client per autenticarsi
al server. L‘interfaccia utente deve avere la funzione ascolto (attivazione del
server) e la funzione chiama per connettersi ad un‘altro utente (attivazione
client). La versione completa del programma prevederà per la funzione ascolto
la registrazione ad un server centrale e per la funzione chiama una selezione tra
gli utenti registrati.
1.1
Descrizione del lavoro svolto
Non è stato realizzato tutto quanto richiesto, ma solo una parte. Di seguito verranno elencate le porzioni effettivamente realizzate. Non è stata implementata
la connessione cifrata con SSL, e di conseguenza la gestione dei certificati. Non
è stata inoltre implementata la procedura di autenticazione, anche se è gia stata
implementata, all’interno del codice, l’interfaccia.
È stata invece realizzata la struttura p2p. Ovvero ogni coppia di utenti comunica direttamente, senza passare, per lo smistamento dei messaggi, attraverso
un server centrale.
Il lavoro è stato svolto utilizzando la libreria Axis, sviluppata dalla Apache
Foundation. Questa libreria consente lo scrittura di WebService e proprio questa tecnologia è alla base dell’architettura di questo progetto, infatti, tutte le
comunicazioni tra i vari componenti dell’architettura si svolgono quindi tramite
il protocollo SOAP, questo vale sia per le comunicazioni Client-Server che per
le comunicazioni Client-Client.
2
Introduzione a SOAP
Obiettivo di SOAP (Simple Object Access Protocol) è fornire agli sviluppatori
una soluzione standard per creare applicazioni distribuite che possano utilizzare
i web services, servizi remoti generalizzati in grado di svolgere i compiti più
disparati. La standardizzazione è d’obbligo in quanto le soluzioni proprietarie
che permettono la distribuzione delle componenti di business logic applicativa,
in particolar modo CORBA e DCOM, soffrono di una serie di difetti che ne
limitano l‘utilizzo ad ampio spettro. Il primo difetto riguarda senza dubbio le
1
caratteristiche di comunicazione che impongono alle soluzioni proprietarie di
utilizzare porte TCP non standard per scambiarsi messaggi informativi, questo
ha il triste effetto collaterale che nel momento in cui si cerca di uscire da una
web-farm per utilizzare un servizio posizionato dall’altra parte di un firewall,
difficilmente si troveranno aperte le porte TCP che CORBA o DCOM si aspettano di trovare. SOAP risolve questo problema proponendo come standard di
comunicazione l‘utilizzo del protocollo HTTP, senza tuttavia precludere l’utilizzo di altri protocolli, a patto che possano trasportare testo. Il secondo difetto
riguarda il formato dei messaggi che le componenti di distribuzione dei protocolli
sopra citati si scambiano per compiere il compito per cui sono stati sviluppati.
Tendenzialmente infatti, si tratta di dati in formato binario non codificato e
questa è un’altra di quelle cose che i firewall non gradiscono e che spesso vietano. SOAP risolve anche questo problema permettendo la serializzazione del
messaggio attraverso l’uso di XML, uno standard anch’esso in piena evoluzione.
XML infatti, essendo testo puro, non ha difficoltà di transito attraverso i firewall
ed è compreso da qualsiasi sistema in grado di leggere testo. Come si può capire
la distribuzione di una simile soluzione può essere davvero universale 1 .
3
Architettura utilizzata nello sviluppo
Per soddisfare il vincolo p2p ovvero una non centralità delle comunicazioni tra
le parti, è stato necessario mettere a conoscenza i singoli utenti degli indirizzi
a cui raggiungere i propri contatti. Questo è possibile solo attraverso l’uso
di quello che nell’implementazione è chiamato Central Server, che si occupa
di memorizzare le informazioni dei singoli utenti. Le informazioni riguardano
alcuni dati personali, che sono facoltativi, ed altri che invece sono obbligatori
come per esempio l’indirizzo email. Ogni utente è identificato da un’alias, o
nickname (questo attributo è obbligatorio), da uno stato (online/offline) e da
un’indirizzo IP. Tutte queste informazioni sono memorizzate in un database.
Nello specifico caso, si è scelto PostgreSQL, ma tutta l’implementazione può
essere portata tranquillamente su altri DBMS. Il codice SQL è molto semplice
e non fa uso di funzionalità avanzate che potrebbero creare problemi. Queste
componenti rappresentano il server centrale.
Per quello che riguarda la parte Client, essa si divide in due componenti.
La parte di ricezione, e la parte di invio messaggi. La parte di gestione della
grafica non viene considerata, per il momento. La parte di ricezione espone una
funzionalità di WebService tramite l’attivazione di un piccolo server in ascolto
sulla porta 8080. Il compito di questa parte è quello di ricevere un messaggio
XML opportunamente strutturato e se tutte le operazioni di parsing effettuate
su questo hanno successo, vengono estratti dal messaggio il mittente e il testo del
messaggio. Una volta estratti questi dati la GUI si occuperà della visualizzazione. La parte di invio invece funziona in modo speculare alla parte di ricezione,
ovvero compone opportunamente un messaggio XML, inserendovi il corpo del
messaggio e il mittente, provvederà successivamente ad inoltrare il tutto al server
in ascolto sul computer del contatto con cui ha scelto di comunicare.
1 Tratto
da http://www.apogeonline.com/webzine/2002/01/24/14/200201241401
2
4
Istruzioni per il deployment
I vari componenti individuati sono il Server Centrale, i Client ed il Database.
Le prime due componenti necessitano delle librerie Axis per poter essere eseguite, quindi i jar-file delle librerie e i class-file del server e del client devono
essere presenti all’interno della variabile d’ambiente CLASSPATH. All’interno di
questa variabile deve comparire anche la libreira xerces per il parsing XML. In
alternativa, per ogni componente, ho creato uno script di avvio, che si occupa di
impostare opportunamente tutto quello che necessario al funzionamento. Come
ultima nota, nel codice generato automaticamente da Axis è codificato l’indirizzo del Server Centrale, che in questo caso è stato posto a 192.168.1.100. Sarebbe
quindi raccomandato porre tutti i componenti su questa classe di indirizzi. Se
il server non dovesse essere raggiungibile con questo indirizzo non sarà possbile
far partire i Client. Inoltre è richiesta una versione del JDK uguale o superiore
alla 1.5.0.
4.1
Database
Come già accennato il database utilizzato è PostgreSQL. Si suppone che questo
sia gia installato e configurato. È necessario a questo punto utilizzare lo script
chiamato DB-CreateAndPopulate.sql che contiene le istruzioni necessarie a
creare un nuovo database, chiamato mydb, contenente una tabella chiamata
utenti necessaria a contenere tutte le informazioni sugli utilizzatori del servizio. La classe java predisposta all’accesso al DB si aspetta inoltre la presenza
di un’utente chiamato dbAccessor, che viene infatti utilizzato per accedere al
DB. Per modificare i vari parametri di connessione è necessario modificare il file
DbAccessor.java e modificare la stringa url e inserire al posto di localhost
l’IP della macchina su cui gira PostgreSQL, al posto di mydb il nome del database in cui cercare la tabella utenti. Eventualmente decommentare la riga
contenente la password, e mettere al posto di XXX la vera password. È gia stata predisposta anche la stringa di connessione per il database MySQL. Si deve
solamente decommentare il codice.
4.2
Server Centrale
Per avviare il Server è possibile utilizzare lo script chiamato SimpleAxisServer.sh
che si preoccupa di far partire il tutto. Per default questo server si pone in ascolto sulla porta 8080, è possibile cambiare questa cosa ma è sconsigliato in quanto
nel codice del Client ci sono molti riferimenti a questa porta per le comunicazioni. Un lavoro futuro sarà quello di creare un file di configurazione per gestire
tutte le opzioni.
4.3
Client
È importante notare che il client ed il server non possonno girare sulla stessa
macchina proprio in virtù del fatto che entrambi espongono dei WebService,
in ascolto sulla porta 8080. In ogni caso, il Client viene fornito sotto forma
di jar-file, con tutte le librerie di cui ha bisogno per il suo funzionamento. Per
farlo partire eseguire il seguente comando: java -jar ClientImpl.jar Questa
parte è stata sviluppata con l’IDE NetBeans, in quanto fornisce un ottimo GUI
3
composer, che ha permesso un rapido sviluppo della parte grafica anche senza
particolari conoscenze da parte del progrmmatore. Questo pone però un piccolo prezzo da pagare sotto forma di un’ulteriore dipendenza da altre libreirie,
comunque fornite dentro alla directory lib.
5
Impostazione dei file
L’impostazione della struttura dei file e delle cartelle non appare ovvia a prima
vista. Questo è dovuto principalmente all’utilizzo atipico che è stato fatto di
Axis e dei Webserver all’interno del progetto.
5.1
Server Centrale
Come punto di partenza prendiamo il ServerCentrale. La procedura di sviluppo
seguita è stata quella suggerita sul sito di Axis alla pagina
http://ws.apache.org/axis/java/user-guide.html, ovvero è stata prima
scritta un’interfaccia, che descrive idealmente tutte le funzionalità che deve offrire il WebService, successivamente questa interfaccia viene elaborata con il
comando java2wsdl (in realtà è un poco più complicato, ma più avanti verrà
spiegato a fondo il meccanismo) per generare tutta la struttura necessaria al funzionamento di Axis. Vengono successivamente generate le classi che si occupano
del Marshalling e della Serializzazione degli oggetti definiti dal programmatore, in un frammento XML. Tra i molti file generati, ne troveremo uno chiamato
NomeProgettoSoapBindingImpl.java che contiene la struttura dei metodi da implementare. Nel nostro caso abbiamo CentralServerSoapBingingImpl che viene
modificato inserendovi la reale implementazione di tutti i metodi definiti nell’interfaccia di cui si è parlato all’inizio. Insieme a tutti questi files vanno poi
aggiunte le classi di servizio, come per esempio nel nostro caso la classe che si
occupa dell’accesso e del’esecuzione delle query al database. Finito di scrivere
tutto quanto si può procedere alla compliazione di tutti i file tramite un semplice
javac *.java.
Anche per questo procedimento, ed in special modo per il passo che riguarda il “passaggio” dall’interfaccia alla struttura di file java finale, che richiede
una certa quantità di opzioni, che a prima vista possono sembrare non banali, ho creato uno script per automatizzare i passaggi. Il nome dello script è
GenWSDL.sh che invocato rispettivamente con gli argomenti
cl Elimina tutti i file che vengono gerati dal comando java2WSDL e i file .class
wsdl Compila il codice delle interfacce ed esegue il comando java2WSDL per generare il file WSDL, successivamente da questo, con il comando WSDL2java
vengono generati tutti i file java necessari per il funzionamento del progetto. Anche questo passaggio verrà spiegato in dettaglio più avanti.
javac Da eseguire una volta completato tutto il file “SoapBindingImpl.java”
client Genera i file necessari per poter scrivere un client che utilizza i servizi
forniti dal server (attraverso la rete e l’uso di SOAP). Più avanti verrà
spiegato il funzionamento, e l’idea che sta alla base del meccanismo.
4
5.2
Client
Il client, come già accennato è stato sviluppato con l’IDE NetBeans, ed è strutturato in tre parti. “GUI”, “Reciver” e “Client”. La parte “GUI” contiene
solamente le interfacce utente e alcuni metodi per adattare i dati sotto forme
visualizzabili dai componenti della GUI. La parte di “Client” contiene le componenti necessarie a dialogare con il ServerCentrale (tutti i file che iniziano
per Server ed il file CentralServerSoapBindingStub.java), le classi di servizio
(Utente.java e AxisThr.java) e la classe per l’invio di messaggi ad altri utenti (MessageSender.java). La parte ”Reciver“ invece contiene la classe che si
occupa della ricezione dei messaggi da parte degli utenti, ovvero di ricevere
un messaggio SOAP (quindi un documento XML), di estrarne le informazioni
necessarie e di comunicare queste informazioni alla GUI, che si occuperà della
visualizzazione. Inoltre è presente un file denominato server-config.wsdd, questo
file, fondamentale per il funzionamento della parte di ricezione messaggi, fa parte dei file che vengono utilizzati da Axis per sapere come rispondere alle varie
richieste che gli vengono sottoposte. Senza questo file il Client sarà lo stesso in
grado di partire e di inviare messaggi ad altri client, ma non sarà in grado di
riceve. È importante che questo file si trovi nella stessa directory da cui viene
lanciato il programma, altrimenti Axis non sarà in grado di trovarlo.
Un’ulteriore problema dovuto all’uso di Axis, è che non è possibile lanciare
il programma dall’IDE in quanto per default questo imposta come directory di
lavoro l’home dell’utente. In questa posizione non si trova però il file sopracitato.
L’unica soluzione è lanciare a mano, da terminale il programma. Oppure cercare
un modo per impostare nel file build.xml (che contiene tutte le direttive per
ant. Un sostituto di make, pensato per lo sviluppo con Java) come directory di
esecuzione quella in cui si trova il file server-config.wsdd.
6
Istruzioni e sequenza per lo sviluppo
Si è certamente notato, a questo punto della lettura che l’impostazione è tutt’altro che chiara e lineare, con molti vincoli e passagi non proprio immediati.
Intendo in queste righe illustrare la sequenza di passaggi necessari alla scrittura e la compilazione dell’applicazione. Prima di cominciare però è oppurtuno
spendere alcune parole sulla struttura di un’applicazione che fa uso di Axis.
Verranno dunque illustrati le soluzioni proposte da Axis e verranno approfondite quelli che sono stati utilizzati nello sviluppo del progetto. Preciso che questo
“riassunto” è tutt’altro che esaustivo, ma è stato scritto per dare un’idea generale sul funzionamento di Axis e per colmare quelle carenze che secondo il
sottoscritto sono presenti nella guida ufficiale. Pertanto, questa è da leggere in
contemporanea a quella ufficiale, oppure in sostituzione di questa in quei punti
poco chiari.
6.1
Axis, concetti e soluzioni
Axis rappresenta la libreria di riferimento per lo sviluppo di WebServices per
il linguaggio Java. Come già accennato si occupa di mettere in comunicazione,
tramite in messaggio XML, un Server ed un Client (detti anche end-point). Axis
tuttavia è solo il nome di una libreria che implementa il protocolo SOAP. Um
messaggio SOAP è composto da varie parti:
5
Envelope Letteralmente, busta, cioè la parte di più esterna del documento
XML, che racchiude tutto ciò che compone il messaggio SOAP.
Header Opzionale. L’intestazione contiene altre informazioni che il uno dei
due end-point si suppone sappia manipolare. In caso contrario è possibile
generare delle eccezzioni.
Body Contiene il vero cuore del messaggio, con tutte le informazioni che si
vogliono fare avere al destinatario.
Attachment Opzionale. In un messaggio SOAP è possibile aggiungere un piccolo allegato, che deve essere serializzato dal programmatore e poi tramite
opportuni metodi viene inserito da Axis all’interno del messaggio XML.
Fault Opzionale. Specifica la o le azioni da svolgere in caso di errore.
SOAP fornisce vari “Stili di servizio” che a loro volta forniscono diversi tipi
di astrazione per il programmatore. Ora come ora può non risultare subito
chiaro il concetto di “stile di servizio”, ma vedrò di spiegare con alcuni esempi
il concetto. Gli “stili” disponibili sono dunque: RPC, Document,Wrapped and
Message.
6.1.1
RPC
RPC, acronimo per Remote Procedure Call, il funzionamento di SOAP sotto
questo aspetto è in tutto per tutto identico a quello dell’RPC tradizionale. Aggiunge solo una piccola “estensione”. Con il meccanismo RPC tradizionale si
doveva, per scrivere il Client, avere i files generati a partire dal server (Stub),
che si occupavano della serializzazione, del marshalling e delle comunicazioni
via rete. Tutto questo è ovviamente possibile in SOAP, ed è quello che viene
utilizzato per implementare le comunicazioni tra i Client ed il Server Centrale.
Tuttavia SOAP, o meglio i WebServices hanno una peculiarità, il documento WSDL. Questo documento è ottenibile interrogando, anche via browser, un
server che espone un WebService. All’interno di questo documento (XML) sono descritti in modo univoco e formale tutte le “funzioni” a disposizione del
programmatore. Quindi è possibile per chiunque scrivere un programma che
utilizza i servizi di un WebService pubblicato in rete. Tutto questo rende totalmente indeipendenti dal linguaggio e dall’architettura tutte le comunicazioni tra
due end-point (in teoria questo sarebbe valido, ma lo standard presenta ancora
alcune lacune, per tutte quelle funzionalità “particolari”, come ad esempio la
serializzazione degli oggetti appartenenti alle “Java Collection”. Di conseguenza
ogni implementazione le colma a proprio modo. Questo tuttavia non invalida il
discorso dell’indipendenza dalla piattaforma e dal linguaggio).
6.1.2
Document e Wrapped
Sono abbastanza simili, infatti entrambi non usano il complesso XML-schema
di SOAP, ma uno più semplice, creato automaticamente a partire dagli oggetti
definiti dal programmatore. Ad esempio, nel progetto è stato definito un tipo
di dato “Utente”, scegliendo Document l’oggetto viene mappato in una entità
XML, che è possibile utilizzare proprio come una classe, con tutti i metodi
annessi. Ovvero nel codice sono libero di scrivere una cosa simile:
6
public void some_funtion(Utente u);
il tipo utente verrà automaticamente serializzato in un segmento del documento
XML che viene inoltrato sulla rete. Scegliendo invece Wrapped, la chiamata alla
funzione sopra verrebbe (idealmente) scomposta in una cosa simile a
7
public void Utente(String nome,
String cognome,
String nick,
String email, ecc);
Questo come si vede è appunto un Wrapper, che consente di costruire l’oggetto
a partire dalle sue componenti. In generale però lo stile più usato dei due è
Document.
6.1.3
Message
Questo tipo di servizio è stato creato per lasciare al programmatore la possibilità
di lavorare con XML. Viene quindi lasciata a lui la composizione di un valido
messaggio XML e il suo parsing. Mentre con tutti gli altri “stili” c’era una certa
libertà nella definizione delle funzioni, il tipo Message rende obbligatorio l’uso
di una di queste definizioni:
public Element[] NOME(Element[] bodies);
public SOAPBodyElement[] NOME(
SOAPBodyElement[] bodies);
public Document NOME(Document body);
public void NOME(SOAPEnvelope req,
SOAPEnvelope resp);
dove al posto di “NOME” è possibile scegliere un qualunque nome per la funzione.
L’importante sono gli argomenti ed il tipo di ritorno. I primi due prendono
entrambi un’array di rispettivamente, Elementi DOM e SOAPBodyElement.
L’importante non è tanto il tipo, ma il fatto che siano array, che consente una
ben precisa metodologia di parsing del contenuto, in questo caso effetuata con
un ciclo while ed una variabile contatore per scorrere tutto il vettore. Il terzo fornisce una rappresentazione tramite albero DOM del Body del messaggio
SOAP. Il quarto metodo prende come parametri due oggetti, che rappresentano rispettivamente il messaggio di richiesta, e il messaggio di risposta. Notare
che è presente tutto il messaggio. Quindi questo metodo è l’unico che consente
di estrarre anche le informazioni contenute nell’header. Idealmente il modo di
procedere quando si utilizza questo metodo è il seguente; si prende la request,
la si parsa e si estraggono le informazioni di cui si necessita per decidere cosa
fare, si esegue “qualcosa” e si compone dentro la response il messaggio SOAP
che deve essere inoltrato indietro a colui che ha fatto la richiesta.
6.2
Strumenti
Il file WSDL (Web Service Deployment Descriptor) è la base che consente di
descrivere in maniera formale tutti i WebService presenti su un determinato server e per ognuno descrivere quali funzionalità mette a disposizione ai
suoi potenziali utilizzatori. Per ottenere questo file si può procedere in due
8
modi. La stesura manuale, oppure si scrive un’interfaccia Java e tramite la
classe org.apache.axis.wsdl.WSDL2Java si crea il file WSDL. Una volta ottenuto il file, è possibile, da questo, partire con lo sviluppo sia della parte
Server-side che di quella Client-side. A questo punto si utilizza invece la classe org.apache.axis.wsdl.WSDL2Java che a partire dal WDSL genera tutta la
struttura di files adatti o per lo sviluppo del server, o per lo sviluppo del client.
Si distingue tra le due tramite un’opzione da passare sulla riga di comando. 2
Per avere un’idea di come vengono usati queste classi dare uno sguardo al file
GenWSDL.sh presente nel progetto.
7
Funzionamento del programma
Descriviamo ora il funzionamento del programma dal punto di vista dell’utente.
Una volta avviato, il Client si pone in ascolto sulla porta 8080 per ricevere i
messaggi degli altri utenti (dimentichiamoci per adesso del fatto che in realtà
c’è SOAP e Axis in mezzo, per semplificare la spiegazione). A questo punto
appare una schermata che permetterà la’utenticazione per accedere al servizio.
Per adesso viene solamente controllato che il nome inserito sia realmente presente nel database, tutta la parte di autenticazione potrebbe (dovrebbe?) essere
gestita tramite LDAP oppute tramite un sistema di Certificati (X509). Una
volta effettuato il “login”, il client deve comunicare al server quale, tra gli indirizzi associati alla maccina locale, desidera usare per essere contattato. Una
volta comunicato questo indirizzo al server, vengono recuperate sempre da quet’ultimo tutte le informazioni sull’utente (nickname, email, lista contatti, ecc),
viene quindi visualizzata una finestra contente la lista contatti. Da questo momento il programma rimane in attesa di un’evento che può essere la selezione di
un contatto dalla lista, oppure la ricezione di un nuovo messaggio. Entrambe
queste azioni aprono una nuova finestra che permette la comunicazione tra due
utenti. C’è tuttavia una differenza tra i due eventi, la selezione di un contatto
locale provoca l’immedita richiesta al sever dell’indirizzo associato al contatto
(se questo è online). Dopodichè ogni messaggio inserito nell’input-box viene
inoltrato al contatto remoto, che lo visualizzerà in una finestra dedicata al dialogo con l’altro utente. All’uscita dal programma viene notificata la propria
disconnessione al server.
2 Fare riferimento a http://ws.apache.org/axis/java/reference.html per la lista e la relativa
descrizione delle opzioni delle classi WSDL2java e java2WSDL
9
Scarica