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