ALMA MATER STUDIORUM - UNIVERSITA' DI BOLOGNA FACOLTA’ DI INGEGNERIA CORSO DI LAUREA IN INGEGNERIA INFORMATICA DIPARTIMENTO DI ELETTRONICA, INFORMATICA E SISTEMISTICA TESI DI LAUREA in Reti di Calcolatori L-A PROGETTO DI STRUMENTI PER LA CONFIGURAZIONE DI APPLICAZIONI JAVA ENTERPRISE CANDIDATO RELATORE: Chiar.mo Prof. Antonio Corradi Andrea Bondi CORRELATORI Ing. Stefano Monti Ing. Samuele Pasini Anno Accademico 2006/2007 Sessione III SOMMARIO 1. Introduzione .................................................................................................................. 3 2. Scelte Tecnologiche...................................................................................................... 5 2.1 Le Applicazioni Multi – Tier .................................................................................. 5 2.2 La Tecnologia Java EE ........................................................................................... 6 2.3 Le Java Annotations................................................................................................ 7 2.4 La Tecnologia Java Management Extension (JMX) ............................................... 8 2.5 L’Application Server JBoss .................................................................................... 9 2.6 Il Linguaggio XML ............................................................................................... 10 2.7 Conclusioni ........................................................................................................... 11 3. I Requisiti dell’Applicazione ...................................................................................... 12 3.1 Il Quadro Generale................................................................................................ 12 3.2 Gestire l’Ordine di Deploy.................................................................................... 12 3.3 Eseguire l’Upload di Componenti ........................................................................ 13 3.4 Chiamare Servizi MBean ...................................................................................... 13 3.5 Invocare Metodi Custom....................................................................................... 13 3.6 Gestire l’Undeploy dei Componenti ..................................................................... 14 3.7 Conclusioni ........................................................................................................... 14 4. La Progettazione ......................................................................................................... 15 4.1 La Struttura dell’Enterprise Java Deployer........................................................... 15 4.2 L’Architettura Client – Server dell’EJD ............................................................... 15 4.3 Il File XML di Configurazione ............................................................................. 16 4.4 Use Case: Upload, Deploy e Configurazione di un’Applicazione ....................... 18 4.5 Use Case: Undeploy di un’Applicazione .............................................................. 20 4.6 Conclusioni ........................................................................................................... 21 5. Implementazione ........................................................................................................ 23 5.1 Il Parser XML ....................................................................................................... 23 1 5.2 Il Sistema di Upload dei File ................................................................................ 25 5.3 Risalire alle Informazioni di Deploy di un pacchetto ........................................... 26 5.4 Risalire all’Indirizzo di Deploy di un Pacchetto ................................................... 27 5.5 Il Deploy tramite MBean ...................................................................................... 27 5.6 L’Undeploy tramite MBean .................................................................................. 28 5.7 L’MBean Principale EjdMainMBean ................................................................... 28 5.8 Conclusioni ........................................................................................................... 31 6. Un Esempio Concreto................................................................................................. 33 6.1 L’MBean di Test ................................................................................................... 33 6.2 L’Applicazione Web di Test ................................................................................. 33 6.3 La Configurazione Custom di Test ....................................................................... 33 6.4 Il File ejd-app.xml................................................................................................. 34 6.5 ScreenShots del Funzionamento ........................................................................... 36 6.6 Conclusioni ........................................................................................................... 38 7. Conclusioni ................................................................................................................. 39 Appendice A: Ordinamento di Oggetti........................................................................... 40 Appendice B: Trasformare un File in un Array di Byte ................................................. 42 Bibliografia ..................................................................................................................... 43 2 1. INTRODUZIONE L’architettura delle applicazioni è una parte degli studi di ingegneria del software in continuo mutamento. Un tempo erano pochi i programmi per computer che facevano affidamento sul collegamento ad altre macchine. Per lo più si trattava di applicazioni eseguite in maniera stand – alone, ovvero completamente autonome, che per lo scambio di informazioni si appoggiavano su supporti esterni. Oppure, all’estremo opposto, si avevano i cosiddetti “thin client”, macchine spesso prive di disco fisso che per il loro funzionamento erano vincolate ad un server centrale, il quale non lasciava loro alcuna possibilità di elaborazione autonoma. Con la grande diffusione di Internet si è trovata una via di mezzo a questi due estremi, grazie alla nascita del mercato del middleware. Applicazioni che si occupano dell’elaborazione delle richieste, lasciando comunque un’autonomia al client ed affidandosi per la gestione dei dati ad un’altra macchina, il database server. Il middleware rappresenta la risposta alla necessità di delegare responsabilità e funzionalità, la cosiddetta “business logic”, a sistemi esterni. Solitamente le applicazioni middleware sono costituite da più componenti, tutti strettamente collegati ed il più delle volte dipendenti l’uno con l’altro. Questo frazionamento del programma apre un problema per quanto riguarda le unità che devono eseguire le varie operazioni: ogni qualvolta è necessario installare o configurare l’applicativo, ciascuna parte del sistema deve essere caricata e configurata nella maniera e nell’ordine corretto. E’ stata quindi valutata l’opportunità di progettare un sistema di deploy e configurazione delle varie componenti che costituiscono l’applicazione. Una sorta di procedura di “installer” capace di gestire anche da remoto e tramite istruzioni facilmente personalizzabili tutte le azioni necessarie per mettere in funzionamento il programma. L’Enterprise Java Deployer – EJD – qui progettato facilita le operazioni di upload, installazione e configurazione di un programma middleware, effettuate seguendo le istruzioni fornite in un semplice file di configurazione. Nel cap. 2 vengono illustrate le principali tecnologie utilizzate, orientate prevalentemente allo sviluppo di applicazioni Java distribuite. Nel cap. 3 si studiano i requisiti dell’applicazione e le richieste che l’EJD deve soddisfare. Sulla base di questo elenco, nel cap. 4 si guarda alle scelte architetturali prese in merito alla progettazione: è in questo passaggio che viene deciso come ed in che ordine verrà data risposta alle richieste precedenti. In seguito, nel cap. 5, viene riportato come, a livello di codice, è stato implementato il sistema richiesto. In questo capitolo sono riportati anche alcuni stralci del codice. Dopo l’implementazione, nel cap. 6, si espone un esempio concreto del funzionamento del programma, con lo sviluppo di una semplice applicazione formata da tre componenti dei quali l’EJD deve gestire l’upload, il deploy e la configurazione. Infine, nel cap. 7, vengono riportate le conclusioni al termine del progetto. Nelle appendici sono ripresi due argomenti utilizzati durante lo sviluppo ma 3 che non sono strettamente pertinenti le tematiche trattate. Nell’appendice A viene descritto come il linguaggio Java gestisce l’ordinamento degli oggetti, mentre nell’appendice B viene riportato come estrarre da un file un array di byte. 4 2. SCELTE TECNOLOGICHE In questo capitolo vengono analizzate le tecnologie che costituiscono le fondamenta dell’applicazione sviluppata. Si fornisce un quadro generale di ciò che sta alla base delle applicazioni e dei servizi web. 2.1 LE APPLICAZIONI MULTI – TIER Il termine multi – tier (o n – tier) indica, nell’ingegneria del software, una particolare architettura che divide il sistema in diversi moduli, ognuno dei quali ha una competenza propria. Solitamente la suddivisione prevede tre componenti, in questo caso viene chiamata architettura three – tier [BauHib]. Si articola in: • • • Presentation Tier (tier 1): si occupa dell’interazione con l’utente, della creazione dell’interfaccia grafica, della visualizzazione dei dati. Nelle applicazioni web il client è solitamente rappresentato dal browser web Business Logic (middle tier): controlla le funzionalità dell’applicazione. E’ costituita da un insieme di moduli, tenuti insieme da un Application Server. Nel caso di web – applications questo può utilizzare la tecnologia Java EE Data Tier (tier 3): si occupa della persistenza dei dati, ovvero del loro salvataggio, aggiornamento e reperimento da un database o un qualche sistema di catalogazione dei dati Una rappresentazione dell’architettura Three – tier è riportata nella figura 2.2.1. Figura 2.1.1: Rappresentazione dell’architettura Three – tier (da esol.com.au) In questa architettura si possono trovare alcune analogie con il design pattern tipico delle interfacce grafiche, ovvero il Model – View – Controller (MVC). I due schemi sono comunque sostanzialmente differenti: l’architettura three – tier è fortemente lineare, si basa sulla regola fondamentale che il client non interagisce mai con il Database Server e tutta la comunicazione passa attraverso il Middle Tier. Diversamente, 5 il MVC è triangolare: il View manda i messaggi al Controller, il Controller aggiorna il Model ed il View viene modificato direttamente dal Model. 2.2 LA TECNOLOGIA JAVA EE Java EE [Jee] è la versione enterprise della piattaforma Java. E’ costituita da un insieme di librerie e specifiche orientate alla costruzione di applicazioni di servizio e web – applications di nuova generazione. La base è costituita dal linguaggio Java con le sue numerose funzionalità, arricchite da altre basate fortemente sullo scambio e l’elaborazione di informazioni tramite la rete. Una fotografia delle tecnologie e delle API che compongono il linguaggio Java si ha in Figura 2.2.1. Figura 2.2.1: L’architettura Java (da www.sun.com) La tecnologie Java EE, fino alla versione 5.0 di Java J2EE [WlsAa], fa uso diffusamente delle seguenti librerie: • • • • Java Servlet [Serv] e Java Server Pages [Jsp], rispettivamente tecnologie per scrivere classi Java e pagine web che utilizzano funzionalità di Java EE Enterprise Java Beans [Ejb], particolari componenti Java con possibilità di fornire servizi di tipo enterprise, come metodi accessibili da remoto, persistenza degli oggetti, modalità di chiamata asincrona Java Transaction Api [Jta], specifica la possibilità di gestire transazioni, ovvero sequenze di operazioni su database con proprietà ACID (Atomicità, Consistenza, Isolamento e Durabilità) Java Message Service [Jms], un insieme di API standard per permettere lo scambio di messaggi tra applicazioni Java 6 Le applicazioni Java EE sono solitamente multi – tier, ovvero distribuite su più macchine, secondo uno schema indicato in Figura 2.2.2. Figura 2.2.2: Architettura multi – tier in un’applicazione Java EE Queste tecnologie sopra elencate costituiscono il cuore della piattaforma Java EE, insieme a JMX, della quale si tratta nel paragrafo 2.4. 2.3 LE JAVA ANNOTATIONS Da evidenziare il grande passo avanti sulla facilità di sviluppo e di deploy grazie alle Annotations [Ann] (o annotazioni), introdotte nella versione 5.0 del linguaggio Java. Si tratta di informazioni in merito ad un programma che non fanno però parte del programma stesso, ovvero che non hanno un effetto diretto sul codice al quale si riferiscono. Sono fondamentalmente di tre tipologie: • • • informazioni per il compilatore; istruzioni da processare al momento di compilazione o di deploy; informazioni necessarie a run time. Le Annotations si riconoscono perché sono introdotte dal simbolo @ seguito dal nome dell’annotazione. Eventualmente, è assegnato anche un valore o una coppia nome – valore. Prima della diffusione delle annotazioni, per specificare le istruzioni di deploy e configurazione di applicazioni java enterprise era continuo il ricorso a numerosi file Xml, che rendevano complicata e ridondante la distribuzione dei programmi. In un 7 secondo tempo, hanno preso piede tecnologie complementari come le XDoclets [Xdoc], sostanzialmente estensioni ai commenti che venivano lette da un pre-processore apposito e grazie alle quali veniva automaticamente generato il file Xml necessario per il deploy. Con la versione 5.0 della piattaforma Java, si è deciso di standardizzare queste estensioni e di renderle parte del linguaggio, permettendo la loro lettura a run time e di fatto eliminando nella maggior parte dei casi la necessità di un file Xml di accompagnamento. 2.4 LA TECNOLOGIA JAVA MANAGEMENT EXTENSION (JMX) JMX – Java Management Extension [SunJmx] – è uno standard Java per la gestione ed il monitoraggio di tutti i componenti, sia hardware che software, compresa la stessa Java Virtual Machine (JVM). E’ definita da due specifiche Java Specification Request strettamente collegate tra loro, sviluppate attraverso Java Community Process ed entrate a far parte della piattaforma J2SE dalla versione 5.0: • • Java Management Extension Specification [JSR3] Java Management Extension Remote Api [JSR160] Tali specifiche suddividono l’architettura JMX in tre livelli separati, come indicato in Tabella 2.4.1. Livello Instrumentation Agent Remote Management Descrizione Le risorse, come applicazioni, dispositivi e servizi, sono rese disponibili usando oggetti Java denominati Managed Bean (MBeans). Gli MBeans espongono le loro interfacce d’amministrazione, composte di attributi e di metodi, attraverso un agente JMX per il management ed il monitoring remoto Il componente principale di un agent JMX è il server MBean. Questo è il nucleo del sistema dove vengono registrati gli MBeans. Un agente JMX include un insieme di servizi per la gestione degli MBeans. Gli agenti JMX controllano direttamente le risorse e le mettono a disposizione degli agent remoti per l’amministrazione Protocol adaptors e standard connectors rendono un Agent JMX accessibile da remoto Tabella 2.4.1: Architettura della tecnologia JMX I primi due livelli, vale a dire Instrumentation ed Agent, sono definiti dalla JSR3, mentre il livello di Remote management è definito dalla JDR160. Un’illustrazione della stessa architettura è in Figura 2.4.1. 8 Figura 2.4.1: Architettura della tecnologia JMX (da www.xmojo.org) Il controllo remoto JMX tramite codice è facilitato da interfacce standard che semplificano la chiamata ai metodi utilizzando la tecnologia Java RMI [Rmi] – Remote Method Invocation – ovvero l’invocazione di metodi java su altre Java Virtual Machines. 2.5 L’APPLICATION SERVER JBOSS JBoss è un’azienda, di proprietà di Red Hat, specializzata nella costruzione di software middleware basato sulla piattaforma Java. Il suo Application Server è rilasciato con licenza GNU LGPL, Open Source. La sua caratteristica fondamentale è la forte modularità, la capacità di poter configurare nei dettagli parti dell’Application Server. Lo stesso JBoss è infatti costituito da un microcontainer sopra al quale si innestano i vari servizi, utilizzando la tecnologia JMX (par. 2.4). La Figura 2.5.1 illustra chiaramente questa architettura, sulla quale noi andremo ad appoggiarci per il nostro strumento di configurazione. 9 Figura 2.5.1: Struttura dell’Application Server JBoss (da “JBoss Configuration Guide”) Il microkernel di JBoss funziona come un server MBean e tutti i servizi forniti sono MBeans che si appoggiano sull’infrastruttura sottostante, rispondente allo standard JMX. Ad esempio, inizialmente JBoss usava Jetty per fornire servlet e jsp. E’ poi passato a Tomcat [Tom], ma è ancora possibile usare Jetty [Jet] come MBean all’interno di JBoss. Una volta caricati dentro a JBoss usando JMX è possibile amministrarli, tramite l’adattatore RMI incluso in JBoss. Jboss è quindi stato scelto valutandone modularità, scalabilità, documentazione [JbDocs] ed affidabilità. La versione utilizzata è la 4.2.2GA, attualmente l’ultima versione stabile. 2.6 IL LINGUAGGIO XML XML [Xml], acronimo di Extensible Markup Language, è un metalinguaggio nato e gestito dal World Wide Web Consortium [W3c], nato come evoluzione e semplificazione dell’SGML. A differenza dell’HTML, utilizzato esclusivamente per la descrizione e la creazione di pagine web ed ipertesti e con un definito gruppo di tag disponibili, il linguaggio XML può essere utilizzato per definire dei propri tag a seconda delle necessità. E’ molto utilizzato per lo scambio dei dati come standard aperto per la trasmissione di informazioni strutturate tramite file di testo, grazie al suo vasto supporto da parte di numerosi linguaggi ed alla sua facilità di lettura, esemplificata nel listato 2.6.1 <?xml version="1.0" encoding="ISO-8859-1"?> <utenti> <utente> <nome>Luca</nome> <cognome>Ruggiero</cognome> <indirizzo>Milano</indirizzo> 10 </utente> <utente> <nome>Max</nome> <cognome>Rossi</cognome> <indirizzo>Roma</indirizzo> </utente> </utenti> Listato 2.6.1: esempio di codice XML I tag utilizzati vengono descritti tramite un altro file creato secondo lo standard XML Schema [Xsch], metodo per la descrizione formale di una grammatica per un linguaggio di markup basato su Xml. Utilizza la stessa sintassi di un documento Xml ma ha dei tag specifici. In questo progetto si è scelto di utilizzare Xml per trasmettere dati ed impostazioni piuttosto che un normale file di testo per l’ampio ed il nativo supporto che il linguaggio Java ha per questo metalinguaggio [MokaXml]. 2.7 CONCLUSIONI In questo secondo capitolo sono state analizzate le tecnologie principali utilizzate nello sviluppo dell’EJD. Trattandosi di un’applicazione Java EE distribuita, particolare rilevanza la coprono le tecnologie fornite dalla piattaforma Java per il controllo remoto, soprattutto la Java Management Extension. Come piattaforma di riferimento è stato scelto l’Application Server JBoss, del quale verranno utilizzate alcune caratteristiche che limitano la portabilità su altri Application Server, quali Web Sphere [WSph] o WebLogic [WLog]. Per gestire la configurazione da parte dell’utente è stato impiegato il linguaggio XML, organico, flessibile e che permette un’agevole lettura delle impostazioni. Inoltre Java dispone di un ampio supporto a questo standard, che ne facilita il parsing. 11 3. I REQUISITI DELL’APPLICAZIONE In questo capitolo vengono analizzati i requisiti richiesti all’applicazione, ovvero le sue funzionalità necessarie. Un’analisi dei requisiti per individuare le linee guida da seguire. 3.1 IL QUADRO GENERALE L’applicazione richiesta deve soddisfare la necessità di pacchettizzare, distribuire e configurare nel minor tempo possibile un programma Java EE complesso basato su JBoss. Per fare questo è necessario suddividere il progetto in due: una parte server che si occupa del coordinamento di tutte le operazioni ed una parte client capace di gestire il trasferimento di un file ed il suo salvataggio in una cartella temporanea. Il deploy, a partire da questa cartella, è competenza del server. Il bisogno di un meccanismo di trasferimento file rende indispensabile che, sulle macchine client, sia già installata una parte dell’EJD. L’applicazione da distribuire è frazionata in più componenti e l’EJD deve saper gestire: • • • • • l’ordine di deploy dei vari componenti. Uno può dipendere dalla corretta installazione di quello precedente; la possibilità di eseguire l’upload di un pacchetto su una macchina remota. Come precedentemente detto, una parte specifica dell’EJD si occuperà di questa operazione; l’esecuzione di servizi MBean sulla macchina remota. Anche questi possono avere una priorità di esecuzione ed uno o più parametri; la chiamata di metodi “custom” per la configurazione. Questo prevede la possibilità di arricchire l’applicazione di base con ulteriori funzionalità; l’undeploy dei componenti. 3.2 GESTIRE L’ORDINE DI DEPLOY Questa è la prima caratteristica richiesta all’EJD. Un’applicazione distribuita è infatti composta da più pacchetti, ognuno responsabile di una parte. Ad esempio, ci sarà sulla macchina A il componente che si occupa dell’interazione con l’utente, sulla macchina B il componente che elabora le richieste e sulla macchina C il componente che interroga ed aggiorna il database. Supponiamo inoltre che, su una quarta macchina D, ci sia un registro dove tutti i componenti si devono iscrivere per essere coordinati. Anche il registro sarà una parte dell’applicazione. In questo quadro è chiara l’importanza centrale rivestita dalla gestione corretta dell’ordine del deploy di queste parti: se non è correttamente configurato il registro, nessuno degli altri componenti potrà iscriversi. Inoltre, in mancanza della possibilità di 12 interrogare il database, è impossibile elaborare delle richieste. Ed ancora, se non si possono elaborare correttamente le richieste, l’interfaccia per l’utente finale non avrà alcuna funzionalità. Nello scenario qui descritto, l’ordine di deploy sarebbe quindi D – C – B – A. Tutte le parti possono essere sulla medesima macchina o su macchine diverse, pratica usuale per le applicazioni con architettura multi – tier. Per questo, oltre alla priorità, ogni componente dovrà avere anche una sua macchina di destinazione. 3.3 ESEGUIRE L’UPLOAD DI COMPONENTI Nello sviluppo di questo strumento non si può dare per scontata la disponibilità, nella macchina di destinazione, del pacchetto corretto del quale effettuare il deploy. Anzi, nello scenario comune è necessario pensare ad una strada per trasferire ogni pacchetto presso la macchina di destinazione. L’upload è ben distinto dal deploy. Jboss prevede, infatti, l’autodeploy di un pacchetto scritto nella sua cartella deploy. Non è però consigliabile trasferire un pacchetto via rete direttamente nella cartella di deploy: potrebbero verificarsi numerose problematiche, principalmente il tentativo di scompattare un archivio trasferito solo parzialmente. E’ quindi necessario trasferire il file presso la cartella “tmp” del JBoss di destinazione. Solamente una volta completato correttamente tutto il trasferimento, la parte server dell’EJD chiamerà il metodo “deploy()” dell’MBean “jboss.system:service=MainDeployer” sulla macchina di destinazione, specificando come argomento il file appena trasferito. La parte client dell’EJD, quindi, sarà sostanzialmente un MBean con la sola capacità di ricevere un array di byte e scriverlo sulla cartella “tmp” locale. 3.4 CHIAMARE SERVIZI MBEAN Una volta eseguito l’upload ed il deploy di un componente, lo stesso ha molto probabilmente la necessità di essere configurato. In un primo caso, si suppone che vi siano a disposizione servizi MBean con dei metodi di configurazione. Sia gli MBean che i metodi da chiamare per ognuno di essi possono essere uno o più. Per questo, come già indicato nel caso dei componenti, anche loro hanno la necessità di poter esprimere una priorità nell’esecuzione. 3.5 INVOCARE METODI CUSTOM Nel caso di particolari configurazioni, può essere necessario avere la possibilità di invocare metodi “custom”, ovvero metodi di classi create ad-hoc per la configurazione. Questi saranno eseguiti sulla macchina server e, nel caso avessero la necessità di dialogare con qualche client, sarà loro compito occuparsi della connessione. 13 Anche in questo caso, ogni classe per la configurazione custom può avere uno o più metodi e parametri. Per questo, analogamente a quanto indicato nel paragrafo precedente, anche nei metodi custom è necessario indicare una priorità di esecuzione. In questo scenario si apre, però, un’altra problematica. L’invocazione di metodi custom viene eseguita utilizzando la tecnologia Reflection di Java [Refl], il che rende la cosa configurabile tramite file xml ma pone un problema di classpath, ovvero come dare la possibilità alla JVM locale di accedere alle classi necessarie. La strada per la soluzione a questo problema è impacchettare le classi con i metodi custom da eseguire in librerie jar, trattate come componenti dell’applicazione da installare. Indicando come host di distribuzione il localhost e dando loro la giusta priorità, vengono installate presso il JBoss localmente in esecuzione come librerie, in modo che, al momento della chiamata tramite Reflection, siano accessibili nel classpath. 3.6 GESTIRE L’UNDEPLOY DEI COMPONENTI Oltre al deploy, l’EJD deve essere in grado di gestire anche l’undeploy dei componenti. In questo caso non si pone il problema dell’upload di file mentre potrebbe essere necessario chiamare servizi MBean o metodi “custom” per eseguire una sorta di shutdown controllato del programma. 3.7 CONCLUSIONI In questo capitolo si è fornito un elenco delle specifiche che l’Enterprise Java Deployer deve soddisfare. Queste sono fondamentalmente tre: • • • Gestire l’upload dei componenti di un’applicazione Gestire l’ordine di deploy / undeploy Facilitare la configurazione tramite chiamate a MBean o metodi custom Con questo quadro delle richieste è possibile passare alla progettazione dell’EJD, ovvero al momento in cui si definiscono i principi di funzionamento dell’applicazione. 14 4. LA PROGETTAZIONE In questo capitolo viene studiata la progettazione dell’EJD. Si tratta di un passaggio fondamentale poiché indica le strade che vengono seguite per soddisfare le specifiche indicate nel capitolo precedente. 4.1 LA STRUTTURA DELL’ENTERPRISE JAVA DEPLOYER Il programma necessita di un punto da dove controllare il procedimento di deploy e configurazione, da dove partire e dare l’impulso iniziale del procedimento. Questa sorta di repository centrale dovrà avere la completa conoscenza della mappa del programma del quale sta per eseguire il deploy, essere in possesso di tutti i componenti e delle istruzioni per la loro configurazione, comprese eventuali classi custom o estensioni. Il repository centrale non entrerà però nel merito del dialogo tra le varie componenti del programma: questo momento riguarda infatti un livello più basso rispetto alla configurazione ed al deploy dell’applicazione. Solamente una volta che i componenti di un determinato programma sono a conoscenza del corretto funzionamento di tutti quelli dai quali dipendono inizieranno infatti a cercare un dialogo tra loro, anche tramite i metodi di configurazione da chiamare. Il cuore centrale dell’EJD, quindi, sarà responsabile del coordinamento di tutte le operazioni. 4.2 L’ARCHITETTURA CLIENT – SERVER DELL’EJD Per lo sviluppo dell’applicazione è stato scelto un orientamento client – server. Questa è la tipologia più classica delle applicazioni distribuite. Sono coinvolte due entità: il server, che eroga il servizio, ed il client che lo richiede. Una rappresentazione di tale architettura è fornita in Figura 4.2.1, dove il server al centro è il computer che si occupa dell’organizzazione ed i client fanno riferimento esclusivamente a lui. 15 Figura 4.2.1: Architettura Client – Server (da www.javaworld.com) Il compito del server, per le necessità del programma, è quello di coordinare il deploy e la configurazione delle varie macchine. Da questo computer, partendo dal presupposto che su tutti gli altri sia già installata e funzionante l’applicazione di gestione dell’upload, vengono caricati i componenti del programma. Il server decide, in base alle istruzioni fornite, l’ordine di deploy e le configurazioni delle parti. L’architettura Client – Server è stata scelta dopo aver valutato la sua alternativa, ovvero il peer to peer. Nel modello peer to peer non ci sono client o server fissi, ma un numero di nodi equivalenti (peer) che fungono sia da client che da server verso altri nodi della rete. Una rappresentazione si trova nella figura 4.2.2, che rende ben evidente come questo modello sia in antitesi rispetto a quello client – server. Figura 4.2.2: Architettura Peer to Peer (da it.wikipedia.org) Questo modello è più complicato da coordinare rispetto al normale Client – Server ma permette il dialogo tra i computer coinvolti. Questo dialogo, per lo scopo della nostra applicazione, non è però necessario: i vari componenti interagiranno con loro ma solo in un momento successivo al loro deploy, che dunque esula dalle competenze dell’EJD. 4.3 IL FILE XML DI CONFIGURAZIONE La configurazione delle modalità di deploy di un’applicazione è passata all’EJD tramite un file XML, chiamato di default ejd-app.xml. La sua struttura deve rispondere allo standard Xml, avendo quindi una radice ed i nodi interni formattati correttamente. La radice è il nodo <project>, che indica quindi l’applicazione di cui fare deploy e da configurare. Ogni applicazione può però avere diverse modalità e necessità di configurazione: possiamo voler configurare solo una parte del programma oppure possiamo voler avere a disposizione diverse configurazioni dello stesso programma. La necessità potrebbe essere anche quella di voler configurare sullo stesso file le istruzioni 16 di deploy e di undeploy dell’applicazione. Per questo ogni <project> potrà avere uno o più <target>, che verrà selezionato al momento dell’esecuzione dell’EJD. Ciascun nodo <target> è composto quindi dalle varie parti del programma, indicate come <component>. Qui deve essere scritto il nome del componente, il file ed il percorso dove si trova, l’host di destinazione e l’ordine di priorità. I componenti vengono eseguiti in ordine crescente, ovvero dal numero 1 in avanti. Su ogni componente può essere necessario eseguire delle azioni. Le principali sono: • • • <upload/> come istruzione per indicare l’intenzione di eseguire l’upload <deploy/> per indicare la volontà di deployare il pacchetto. E’ un’istruzione solitamente eseguita in seguito alla precedente di upload. Per eseguirla senza chiamare la precedente è necessario avere la certezza dell’esistenza del pacchetto indicato sulla macchina di destinazione <undeploy/> per eseguire l’undeploy del componente indicato. Le azioni di configurazione sono indicate con i tag <mbean> e <custom>. Per quanto riguarda il primo sarà necessario indicare l’objectName, mentre nel secondo caso si indicherà la classe da ricercare. Anche per queste configurazioni viene indicata la priorità in maniera crescente. L’ordine di priorità non fa distinzione tra configurazioni di tipo <mbean> e di tipo <custom>. Ogni configurazione ha la possibilità di chiamare uno o più metodi, anch’essi ordinati con priorità crescente, ognuno con uno o più parametri. In figura 4.3.1 si trova una rappresentazione ad albero di un tipico file di configurazione ejd-app.xml 17 Figura 4.3.1: Struttura ad albero di un file di configurazione ejd-app.xml 4.4 USE CASE: UPLOAD, UN’APPLICAZIONE DEPLOY E CONFIGURAZIONE DI Il primo Caso di studio si inquadra nello scenario più classico, ovvero l’upload, il deploy e la configurazione di un componente. I passaggi sono indicati nel diagramma in Figura 4.4.1 e di seguito: 1. selezione, da parte dell’utente, del target desiderato. Questo viene effettuato tramite una chiamata al metodo execute() dell’MBean principale EjdMainMBean, indicando come argomento il target voluto; 2. processo del file xml di configurazione e costruita la sua rappresentazione tramite un albero ordinato di oggetti; 3. il primo componente per priorità viene processato. Prima di tutto viene controllato se è necessario eseguirne l’upload. In caso di risposta affermativa, il componente viene caricato sulla macchina di destinazione; 4. dopo l’upload, viene controllato se di questo componente è richiesto il deploy. Per configurazioni particolari può infatti non essere richiesto il deploy di alcun componente ma solo l’esecuzione di metodi MBean o Custom dei quali si sa già 18 l’esistenza sulla macchina client. Nel caso invece in cui si chieda il deploy questo è reso possibile tramite chiamate RMI al metodo “deploy()” dell’MBean di JBoss “jboss.system:service=MainDeployer”. Prima di eseguire il deploy di un pacchetto è necessario verificare che lo stesso non sia già in esecuzione nella macchina di destinazione. In tal caso ne viene effettuato prima l’undeploy; 5. ogni componente, dopo l’eventuale upload e deploy, può avere la sua configurazione. Questa può essere di due tipi: MBean oppure Custom. Nel caso di chiamate ad uno o più metodi MBean si stabilisce una connessione RMI al server di destinazione e vengono eseguiti i metodi richiesti. In una configurazione Custom, la chiamata avviene tramite le Reflection API. Per tale motivo è indispensabile avere nel proprio classpath locale le classi che vengono utilizzate. La libreria utilizzata può essere inclusa nel pacchetto dell’EJD, e quindi immediatamente utilizzabile, oppure caricata sul localhost come un componente dell’applicazione del quale eseguire il deploy. Entrambe le soluzioni funzionano e permettono alle Reflection API di accedere ai metodi richiesti; 6. per ogni componente vengono ripetuti i punti dal 3 al 5. 19 Figura 4.4.1: Diagramma di flusso del Case Study di Upload, Deploy e Configurazione di un’applicazione 4.5 USE CASE: UNDEPLOY DI UN’APPLICAZIONE Nel caso di undeploy di un’applicazione, la possibile richiesta è quella di eseguire alcuni metodi di configurazione per eseguire uno shutdown pulito. In questo caso, l’utente specificherà un componente privo di filename e filepath ma con priorità alta. Di questo componente l’utente non dovrà indicare né l’upload né il deploy 20 ma solo la configurazione, tramite tag <mbean> o <custom>. Queste istruzioni conterranno i metodi da eseguire prima dell’undeploy vero e proprio. In seguito, al componente effettivo del quale eseguire l’undeploy viene assegnata una priorità più bassa. L’EJD ricercherà tra le applicazioni deployate presso la macchina di destinazione quella che fa riferimento al file indicato. Una chiamata RMI al metodo “undeploy()”dell’MBean di JBoss “jboss.system:service=MainDeployer” permetterà di disinstallarla. Uno schema di tale funzionamento è fornito in Figura 4.5.1. Figura 4.5.1: Use Case di undeploy di un’applicazione 4.6 CONCLUSIONI Scopo di questo capitolo era chiarire, tramite anche l’ausilio di schemi, il funzionamento dell’EJD. Dopo lo studio delle specifiche, è stata scelta un’architettura 21 client – server, che facilita il coordinamento delle operazioni. E’ stata studiata anche la struttura che deve seguire il file xml di configurazione per soddisfare tutte le richieste possibili da parte dell’utente. Ultimi passaggi sono stati la descrizione nei particolari dei due funzionamenti principali del programma, ovvero la fase di deploy e configurazione e quella di undeploy. 22 5. IMPLEMENTAZIONE In questo capitolo, dedicato all’implementazione, viene descritto come quanto indicato nei due capitoli precedenti, ovvero “Requisiti dell’Applicazione” e “Progettazione”, viene tradotto in codice. Sono riportati alcuni listati, le parti di codice più significative per comprendere il funzionamento. 5.1 IL PARSER XML Il lavoro dell’EJD parte dal Parser XML, ovvero il motore che, prendendo il file di configurazione, costruisce l’albero di oggetti che rappresenta l’applicazione da configurare. E’ stata scelta come tecnologia di accesso al file Xml DOM – Document Object Model [Dom] – che permette la navigazione di un file Xml come un albero di oggetti. In seguito, in modo iterativo, questa struttura viene tradotta in un vero albero di oggetti predefiniti a seconda del loro tag. Alternativa a DOM è la tecnologia SAX – Simple Api for XML [Sax] – che permette l’accesso sequenziale ad un file xml. Questo rende più difficile la costruzione di un albero come desiderato ma, nel caso di documenti molto grandi, velocizza e facilita la lettura. Essendo i file xml di configurazione per l’EJD di dimensioni relativamente ridotte, non si pone questo problema. E’ stata quindi scelta la tecnologia DOM. Il parsing del file di configurazione viene eseguito tramite una chiamata al metodo parse(), del quale la firma si trova nel Listato 5.1.1 public ArrayList<Component> parse(String file, String target) Listato 5.1.1: Firma del metodo parse() La prima operazione eseguita dal Parser è la ricerca del root tag <project>. In seguito, tramite le istruzioni indicate nel listato 5.1.2, si risale al target richiesto. NodeList targets = projects.getElementsByTagName("target"); Element e; /* * Legge tutti i target e cerca quello richiesto */ for (int i = 0; i < targets.getLength(); i++) { e = (Element) targets.item(i); if (!e.getAttribute("name").equalsIgnoreCase(target)) continue; /* * Una volta trovato il target richiesto, processa e ordina * tutti i componenti */ 23 NodeList compNodes = e.getElementsByTagName("component"); for (int j = 0; j < compNodes.getLength(); j++) components.add(parseComponent(((Element) compNodes.item(j)))); Collections.sort(components); } Listato 5.1.2: Ricerca del target richiesto e parsing iterativo dei componenti Una volta trovato il target, come si legge dal Listato 5.1.2 si esegue il parsing di ogni componente tramite la chiamata al metodo parseComponent(Element e). Questo metodo crea un nuovo oggetto Component, ne imposta le proprietà secondo gli attributi indicati e legge i suoi nodi figli. Possono essere di due tipologie: <mbean> oppure <custom>. Nel primo caso, il metodo chiamato è parseMBeanConfig(), mentre nel secondo viene chiamato il metodo parseCustomConfig(). Questi due metodi differiscono per gli oggetti creati e per alcuni attributi che vengono impostati, ma entrambi controllano tra i nodi figli i metodi da eseguire con i relativi parametri. L’ArrayList creata all’interno del Component, con i configuratori MBeanConfig e CustomConfig, è unica. Le due classi sono infatti entrambe estensioni della classe Config. Questo permette di ordinare i metodi da chiamare secondo la priorità, senza fare distinzione tra metodi di configurazione MBean o Custom. Terminato il ciclo di analisi di tutti i componenti, l’ArrayList costruita viene ordinata secondo le priorità dei componenti (Rif. Appendice A) e restituita al chiamante. Il funzionamento del Parser è schematizzato in Figura 5.1.1. 24 Figura 5.1.1: Diagramma di flusso del funzionamento del Parser 5.2 IL SISTEMA DI UPLOAD DEI FILE L’unica parte client dell’EJD è rappresentata dal sistema di upload di un file. Questo permette di avere un interlocutore certo da parte della macchina di destinazione sul quale contare per il trasferimento del file contenente il componente. 25 Si tratta di un MBean contenente un metodo fileUpload() con la firma indicata in listato 5.2.1. public boolean fileUpload(byte[] bytes, String filename) Listato 5.2.1: Firma del metodo fileUpload La prima operazione del metodo è la ricerca della cartella temporanea, tramite l’accesso via RMI al server JBoss locale indicato nel listato 5.2.2. In questa cartella viene in seguito scritto tramite un oggetto FileOutputStream l’array di byte passato come argomento, in un file chiamato come l’argomento passato che, se esistente, viene cancellato. InitialContext ic; ic = new InitialContext(); MBeanServerConnection server = (MBeanServerConnection) ic .lookup("jmx/invoker/RMIAdaptor"); ObjectName obj = new ObjectName("jboss.system:type=ServerConfig"); File tempDir = (File) server.getAttribute(obj, "ServerTempDir"); Listato 5.2.2: Ricerca della cartella Temp del Jboss locale L’MBean configurato è reperibile all’indirizzo “ejd:service=ejdClient”, come indicato dalle direttive iniziali riportate nel listato 5.2.3 @Service(objectName = "ejd:service=ejdClient") @Management(EjdClient.class) public class EjdClientMBean implements EjdClient { Listato 5.2.3: Le direttive iniziali per indicare il servizio MBean Dal lato server, l’operazione principale è quella di trasformare un file in un array di byte (rif. Appendice B), e passare questo array come argomento insieme al nome del file da scrivere sul client. 5.3 RISALIRE ALLE INFORMAZIONI DI DEPLOY DI UN PACCHETTO Risalire alle informazioni di Deploy di un pacchetto è una pratica utilizzata, nell’EJD, in due casi: • • Verificare se di un pacchetto è già stato eseguito il deploy Ottenere le informazioni necessarie per l’undeploy di un pacchetto Viene effettuata ricercando, all’interno della lista di tutte le applicazioni installate nella macchina interessata, quella che fa riferimento al file indicato. La lista si ottiene tramite 26 una chiamata all’MBean di JBoss “jboss.system:service=MainDeployer”. Questa operazione è indicata nel Listato 5.3.1. ObjectName deplo; deplo = new ObjectName("jboss.system:service=MainDeployer"); Object[] arg = {}; String[] sign = {}; List<DeploymentInfo> deployed = (List<DeploymentInfo>) server .invoke(deplo, "listDeployed", arg, sign); /* * Scorro tutta la lista alla ricerca del filename che mi serve */ for (DeploymentInfo item : deployed) { if (item.url.getPath().contains(filename)) return item; } } Listato 5.3.1: risalire al DeploymentInfo di un pacchetto 5.4 RISALIRE ALL’INDIRIZZO DI DEPLOY DI UN PACCHETTO Per accedere tramite codice a file e archivi contenuti nel pacchetto di distribuzione del progetto è necessario risalire alla cartella o all’archivio di deploy dell’ear o jar. Per fare questo bisogna trovare il ClassLoader di una istanza di una classe del progetto, creata ad – hoc o accedendo alla stessa istanza in esecuzione tramite la keyword this. Viene a questo punto in aiuto una classe di JBoss, org.jboss.mx.loading.UnifiedClassLoader, che è il ClassLoader delle istanze eseguite all’interno del server JBoss [JBServ]. Questa è un’estensione della classe java.net.URLClassLoader che fornisce l’opportunità di individuare con il metodo getUrl().getPath() l’indirizzo locale dove è localizzato il Class Loader. Questo indirizzo è proprio il path cercato del deploy dell’applicazione. Un esempio di questo sistema è fornito nel listato 5.4.1 UnifiedClassLoader cl = (UnifiedClassLoader) new Parser().getClass().getClassLoader(); String deployDir = cl.getURL().getPath(); Listato 5.4.1: risalire alla directory di deploy. L’oggetto Parser è un oggetto contenuto nell’EJB 5.5 IL DEPLOY TRAMITE MBEAN Il deploy di un pacchetto, nell’Application Server JBoss, è solitamente eseguito tramite la copia del pacchetto stesso nella cartella “deploy” del server e la decisione dei tempi e modi è delegata al meccanismo di Hot Deployment [HtDe]. Nel caso dell’EJD, è stata 27 scelta la possibilità di gestire il deploy delle applicazioni tramite chiamate al metodo deploy dell’MBean di JBoss “jboss.system:service=MainDeployer” poiché: • • Questo permette una maggiore flessibilità ed autonomia nel deploy, separando il momento dell’upload da quello del deploy Con l’Hot Deployment vi è il rischio, già precedentemente indicato, di un deploy del pacchetto su una macchina remota quando ancora questo non è stato completamente trasferito, causando così un errore dell’Application Server e pregiudicando la corretta configurazione del sistema La chiamata al metodo deploy() ha un solo argomento, che è l’indirizzo del file del quale eseguire il deploy. Il pacchetto viene cercato nella cartella temp remota e con il nome indicato nell’attributo filename del tag <component>. Le righe di codice che effettuano il deploy sono riportate nel listato 5.5.1. ObjectName deplo = new ObjectName( "jboss.system:service=MainDeployer"); Object[] arg = { filename }; String[] sign = { "java.lang.String" }; server.invoke(deplo, "deploy", arg, sign); Listato 5.5.1: deploy di un file tramite chiamata MBean 5.6 L’UNDEPLOY TRAMITE MBEAN L’interazione con l’MBean di JBoss “jboss.system:service=MainDeployer” è necessaria anche per quanto concerne la fase di undeploy. Nel caso dell’undeploy viene chiamato il metodo undeploy(), al quale viene passato come argomento il DeploymentInfo del pacchetto interessato, ottenuto come indicato nel paragrafo 5.3. Una volta in possesso del DeploymentInfo corretto, l’istruzione è semplice e si ritrova nel listato 5.6.1. ObjectName deplo = new ObjectName( "jboss.system:service=MainDeployer"); Object[] arg = { info }; String[] sign = { "org.jboss.deployment.DeploymentInfo" }; server.invoke(deplo, "undeploy", arg, sign); Listato 5.6.1: undeploy di un’applicazione 5.7 L’MBEAN PRINCIPALE EJDMAINMBEAN Il cuore dell’applicazione è rappresentato dall’MBean EjdMainMBean. Questo implementa l’interfaccia EjdMain che descrive tre metodi, indicati nel listato 5.7.1. public void execute(String filename, String target); 28 public void execute(String target); public void create() throws Exception; Listato 5.7.1: Le firme dei metodi dell’interfaccia EjdMain I primi due metodi execute() rappresentano un esempio di Overloading [Overl] dei metodi in Java. Nel caso in cui venga chiamato con due argomenti, il primo rappresenta l’indirizzo del file xml di configurazione ed il secondo il target da eseguire. Se invece lo stesso metodo execute() viene chiamato con solo un argomento, questo indica il target da eseguire e come file xml di configurazione viene preso di default il nome ejdapp.xml all’interno dell’archivio di deploy dell’EJD. Il metodo create() proviene invece dalle specifiche per gli MBean [Ej3Tr]. Viene chiamato quando si crea il servizio dall’Application Server. In questo caso viene utilizzato all’interno dell’MBean per due scopi: • • • risalire alla cartella Temp locale, in maniera analoga a quanto già illustrato nel listato 5.2.2; risalire all’indirizzo di deploy dell’EJD (vedi Par. 5.4); individuare se il programma è deployato tramite un archivio compresso o tramite una cartella. Queste ultime due necessità sono dovute al bisogno di individuare l’indirizzo dei componenti distribuiti insieme al pacchetto dell’EJD e, nel caso questo sia stato deployato come jar e non come cartella, estrarre dall’archivio i file necessari. All’interno del metodo execute(), la prima chiamata riguarda il parsing del file xml desiderato, effettuato come indicato nel paragrafo 5.1. Il risultato di un corretto parsing è una ArrayList di Component ordinata per priorità. Il ciclo for principale è incaricato di scorrere l’intera lista e, per ogni componente, eseguire le richieste. La prima operazione è l’apertura di una connessione al server MBean di destinazione, sia esso localhost o una macchina remota. Questo permette di avere un oggetto MBeanServerConnection al quale viene fatto riferimento per ogni operazione successiva. Le chiamate avvengono utilizzando le api RMI. Nel listato 5.7.2 è indicato come aprire questa connessione. Context ic; Properties contextProperties; String hostname = c.getHostname(); contextProperties = new Properties(); contextProperties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); contextProperties.setProperty(Context.PROVIDER_URL, "jnp://" + hostname + ":1099"); ic = new InitialContext(contextProperties); MBeanServerConnection server = (MBeanServerConnection) ic .lookup("jmx/invoker/RMIAdaptor"); 29 Listato 5.7.2: Apertura di un canale MBeanServerConnection I passi successivi, eseguiti tramite la connessione aperta, sono la ricerca della cartella “ServerTempDir” remota e delle eventuali informazioni di deploy del componente, nel caso sia già installato. Quest’ultima operazione è possibile tramite la chiamata al metodo listDeployed() dell’MBean di JBoss “jboss.system:service=MainDeployer”. Il metodo restituisce una lista di DeploymentInfo che viene navigata fino al ritrovamento di un descrittore di deploy che faccia riferimento al file indicato. Nel caso non ve ne siano, il risultato sarà null. Maggiori informazioni su questa pratica sono state fornite nel Paragrafo 5.3. Il seguito del funzionamento dell’EjdMainMBean è quello indicato in figura 4.3.1. Per ogni componente viene eseguito l’upload, se richiesto, e l’eventuale deploy o undeploy. Come prima, se un componente è già deployato, prima di effettuarne il deploy si disinstalla dalla macchina di destinazione. Allo stesso modo non viene effettuato il deploy se il componente indicato non risulta essere deployato. Dopo questi passaggi, il punto in seguito più importante è la configurazione. Questa può essere di due tipi: • • tramite MBean, con oggetti MBeanConfig; tramite chiatate “custom”, con oggetti CustomConfig. L’oggetto Component ha però una sola ArrayList di Config, strada dettata dalla necessità di non effettuare distinzioni in priorità tra configurazioni MBean e Custom. Nella navigazione di questa lista, quindi, il primo passaggio è quello di distinguere le due strade di configurazione, tramite il codice in listato 5.7.3. /* * Per ogni componente, è possibile chiamare più di un metodo di * configurazione, ordinati per priorità. */ for (Config conf : c.getConfig()) { if (conf instanceof MBeanConfig) { /* * Configurazione MBean */ } else if (conf instanceof CustomConfig) { /* * Configurazione Custom */ } } Listato 5.7.3: Distinzione tra configurazione MBean e Custom Iniziando con il caso di configurazioni tramite MBean, il metodo è quello utilizzato finora di chiamate RMI e metodi di MBean con la connessione 30 MBeanServerConnection precedentemente creata. Ogni configurazione MBean può richiedere l’invocazione di uno o più metodi, ordinati con priorità crescente. Ciascuno di questi avrà la sua firma, il numero di argomenti richiesti ed il loro tipo. La chiamata utilizzata è indicata nel listato 5.7.4, dove server è la connessione creata, object è l’ObjectName instanziato come indicato negli attributi del tag <mbean>, met è l’oggetto MethodConf che rappresenta il tag <method>, ovvero il metodo da chiamare, paramValue è una lista contenente i valori dei parametri e strType è un array che indica, ad indice corrispondente nei valori dei parametri, il tipo. server.invoke(object, met.getName(), paramValue.toArray(),strType); Listato 5.7.4: Chiamata di una configurazione MBean Nel caso di configurazione “custom” l’accesso avviene tramite le Reflection API di Java. La prima attenzione da dedicare a questo passaggio è la certezza, da parte dell’utente, di avere disponibile all’interno del classpath locale la classe creata per la configurazione. Questa può essere impacchettata in un jar del quale eseguire il deploy esattamente come un componente. Risolta questa problematica, il procedimento è per certi versi analogo alla precedente configurazione tramite MBean. Tramite il codice in listato 5.7.5 si crea un’istanza della classe voluta, sulla quale si opera. CustomConfig cc = (CustomConfig) conf; Class<?> cls = Class.forName(cc.getClassName()); Object object = cls.newInstance(); Listato 5.7.5: Creazione di un’istanza tramite Reflection A sua volta ogni configurazione custom può avere uno o più metodi da eseguire, ai quali si accede tramite un ciclo iterativo for. Per risalire al metodo da invocare, bisogna chiamare il getDeclaredMethod() sull’oggetto rappresentante la classe, indicando negli argomenti il nome del metodo ed il tipo dei suoi argomenti, ovvero la firma del metodo. Sull’oggetto Method restituito viene eseguito il metodo invoke() con argomenti l’oggetto di destinazione ed un array con gli argomenti. Terminata la chiamata a tutte le configurazioni indicate, si passa al componente successivo, fino al termine dei componenti. 5.8 CONCLUSIONI In questo capitolo si è visto, con l’ausilio anche di ampi stralci di codice, il funzionamento del programma. Sono state indicate le strade scelte per la soddisfazione delle specifiche, in particolare la struttura del parser XML, la gestione dell’upload dei 31 file, del deploy e dell’undeploy tramite chiamate a metodi di servizi MBean, sviluppati ad – hoc o forniti dalla piattaforma JBoss. Una volta descritto il sistema di gestione di questi problemi specifici, si è studiato il codice dell’MBean principale dell’applicazione, chiamato EjdMainMBean, che si occupa di tutto il coordinamento delle operazioni. 32 6. UN ESEMPIO CONCRETO Per descrivere il funzionamento dell’Enterprise Java Deployer in uno scenario possibile si è creata un’applicazione distribuita molto semplice costituita da parti di varia natura. 6.1 L’MBEAN DI TEST La prima parte del progetto d’esempio è costituita da un MBean. Questo non fa nulla, se non avere un metodo al suo interno che prende una stringa e la scrive sullo standard output. Il codice è riportato nel listato 6.1.1 @Service(objectName="test:service=test") @Management(Test.class) public class TestMBean implements Test { public void echo(String echo) { System.out.println("Metodo echo MBean:" + echo); } } Listato 6.1.1: codice di TestMBean.java Questo MBean è impacchettato nell’archivio TestMBean.jar, successivamente rinominato TestMBean.comp e trasferito nella directory /tmp/. 6.2 L’APPLICAZIONE WEB DI TEST Il secondo pacchetto che compone il nostro ambiente di test è una semplice pagina web che riporta il classico testo “Ciao Mondo” sul browser. Impacchettata in Prova.war, successivamente rinominato Prova.comp e inserito nella cartella di base del nostro pacchetto EnterpriseJavaDeployer.jar. 6.3 LA CONFIGURAZIONE CUSTOM DI TEST Il terzo ed ultimo pacchetto del test è un jar contenente una semplice classe di prova, che rappresenta il caso di una chiamata ad una configurazione custom. Il codice della classe è riportato nel listato 6.3.1. package testejd; public class Prova { public void scrivi(String echo) { System.out.println("Custom scrive: " + echo); } public void parla(String echo) { System.out.println("Custom parla: " + echo); } } 33 Listato 6.3.1: Codice di Prova.java Anche questa classe è compilata ed inserita in un jar rinominato TestCustom.comp e trasferito nella cartella /tmp/. 6.4 IL FILE EJD-APP.XML Le istruzioni per la configurazione della nostra applicazione, costituita dai tre pacchetti sopra indicati, sono all’interno del file ejd-app.xml. Per comodità il file viene inserito all’interno del pacchetto dell’EJD ma, come indicato nel paragrafo 5.3, è possibile specificarne un indirizzo differente. Analizziamo punto per punto il file di configurazione. Nel listato 6.4.1 si trova l’inizio del file xml con la dichiarazione del nodo root <project> e del primo target, al quale è assegnato il nome di deploy. <?xml version="1.0" encoding="UTF-8"?> <project name="Prova"> <target name="deploy"> Listato 6.4.1: Apertura del file ejd-app.xml Proseguendo, nel listato 6.4.2 si incontra il primo componente che costituisce l’applicazione da configurare. Si tratta del pacchetto descritto nel paragrafo 6.2, salvato in un file di nome Prova.comp che si trova all’interno dell’archivio EnterpriseJavaDeployer.jar. Per questo motivo, tra gli attributi del componente non è specificato il filepath. Del pacchetto viene effettuato l’upload ed il deploy sulla macchina localhost. <!-- Il primo componente è interno all'archivio --> <component name="Prova.comp" priority="4" filename="Prova.war" hostname="localhost"> <upload /> <deploy /> </component> Listato 6.4.2: La descrizione del primo componente Il listato 6.4.3 riporta la configurazione della libreria che verrà utilizzata nella configurazione del componente successivo, come indicato nel paragrafo 6.3. Questa è in un archivio di formato jar, rinominato in TestCustom.comp. Il file si trova nella cartella /tmp/. Da notare l’alta priorità assegnata a questo componente, il quale, pur trovandosi in righe successive al componente Prova.comp, verrà deployato prima. <component name="TestCustom.comp" priority="1" filename="TestCustom.jar" filepath="/tmp/" hostname="localhost"> <upload /> <deploy /> </component> 34 Listato 6.4.3: Il deploy della libreria TestCustom.jar L’ultimo componente è l’unico del quale viene richiesta la configurazione, TestMBean.comp. La sua priorità è 2, verrà quindi deployato in seguito a TestCustom.comp ma prima di Prova.comp. Nel listato 6.4.4 è riportata la parte di configurazione tramite MBean, la quale richiede l’esecuzione del metodo echo dell’MBean con ObjectName test:service=test. Questo MBean è stato configurato con il deploy dello stesso componente TestMBean.comp, effettuato qualche attimo prima. Il risultato sarà la stampa sullo standard output del valore del parametro passato, ovvero “scrivi da MBean”. Il funzionamento di questo MBean è riportato nel paragrafo 6.1. <!-- Configurazione MBean --> <mbean objectName="test:service=test" priority="1"> <method name="echo" priority="1"> <param type="java.lang.String" value="scrivi da MBean" /> </method> </mbean> Listato 6.4.4: Configurazione MBean Nel listato 6.4.5 è invece riportata la configurazione tramite la chiamata per Reflection ai metodi scrivi e parla della classe testejd.Prova, del quale è stato effettuato il deploy con il componente TestCustom.comp. Da notare la priorità, con un valore più basso rispetto alla configurazione MBean e quindi eseguita successivamente. Anche i due metodi di configurazione hanno priorità differente. <!-- Configurazione Custom --> <custom class="testejd.Prova" priority="2"> <method name="scrivi" priority="2"> <param type="java.lang.String" value="scrivi da custom" /> </method> <method name="parla" priority="1"> <param type="java.lang.String" value="parla da custom"/> </method> </custom> Listato 6.4.5: Configurazione Custom Nel listato 6.4.6 viene specificato un ulteriore target, undeploy, per eseguire l’undeploy di tutti e tre i componenti. <target name="undeploy"> <component name="Prova.comp" priority="4" filename="Prova.war" filepath="/tmp/" hostname="localhost"> <undeploy /> </component> <component name="TestCustom.comp" priority="1" filename="TestCustom.jar" filepath="/tmp/" hostname="localhost"> <undeploy /> </component> <component name="TestMBean.comp" priority="2" filename="TestMBean.jar" filepath="/tmp/" 35 hostname="localhost"> <undeploy /> </component> </target> Listato 6.4.6: Undeploy dei componenti 6.5 SCREENSHOTS DEL FUNZIONAMENTO In questo paragrafo vengono riportati alcuni screenshots del funzionamento del programma, eseguito su AS JBoss 4.2.3GA tramite l’utilizzo dell’IDE MyEclipse. Figura 6.5.1: Il contenuto dell’archivio EnterpriseJavaDeployer.jar 10:50:41,359 INFO [JmxKernelAbstraction] creating wrapper delegate for: org.jboss.ejb3.service.ServiceContainer 10:50:41,375 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=EnterpriseJavaDeployer.jar,name=EjdClientMBean,service= EJB3 with dependencies: 10:50:41,515 INFO [EJBContainer] STARTED EJB: ejd.client.EjdClientMBean ejbName: EjdClientMBean 10:50:41,578 INFO [JmxKernelAbstraction] creating wrapper delegate for: org.jboss.ejb3.service.ServiceContainer 10:50:41,578 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=EnterpriseJavaDeployer.jar,name=EjdMainMBean,service=EJ B3 with dependencies: 10:50:41,609 INFO [EJBContainer] STARTED EJB: ejd.server.EjdMainMBean ejbName: EjdMainMBean 10:50:41,656 INFO [EJB3Deployer] Deployed: file:/C:/jboss4.2.3.GA/server/default/deploy/EnterpriseJavaDeployer.jar 36 Listato 6.5.1: La Console di JBoss nel Deploy di EnterpriseJavaDeployer.jar Figura 6.5.2: La jmx-console dell’MBean ejd:service=ejdMain 11:01:16,531 INFO [JmxKernelAbstraction] creating wrapper delegate for: org.jboss.ejb3.service.ServiceContainer 11:01:16,531 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=TestMBean.jar,name=TestMBean,service=EJB3 with dependencies: 11:01:16,562 INFO TestMBean [EJBContainer] STARTED EJB: TestMBean ejbName: 11:01:16,625 INFO [EJB3Deployer] Deployed: file:/C:/jboss4.2.3.GA/server/default/tmp/TestMBean.jar 11:01:16,625 INFO [STDOUT] Metodo echo MBean:scrivi da MBean 11:01:16,640 INFO [STDOUT] Custom parla: parla da custom 11:01:16,640 INFO [STDOUT] Custom scrive: scrivi da custom 11:01:52,921 INFO [TomcatDeployer] deploy, warUrl=.../tmp/deploy/tmp19227Prova-exp.war/ ctxPath=/Prova, 37 Listato 6.5.2: La Console di JBoss dopo la chiamata al metodo execute(“deploy”) 11:06:46,843 WARN registered [JmxKernelAbstraction] test:service=test is not 11:06:46,843 INFO TestMBean [EJBContainer] STOPPED EJB: TestMBean ejbName: 11:06:46,843 WARN [JmxKernelAbstraction] jboss.j2ee:jar=TestMBean.jar,name=TestMBean,service=EJB3 is not registered 11:06:47,765 INFO [TomcatDeployer] undeploy, ctxPath=/Prova, warUrl=.../tmp/deploy/tmp19227Prova-exp.war/ Listato 6.5.3: La Console di JBoss dopo la chiamata al metodo execute(“undeploy”) 6.6 CONCLUSIONI In questo capitolo è stato fornito un esempio di funzionamento dell’Enterprise Java Deployer, tramite l’upload, il deploy e la configurazione di un’applicazione di test. Sono state testate tutte le richieste che possono essere avanzate da parte di un utente finale, comprese entrambe le configurazioni tramite MBean e Custom. Nell’esempio in questione queste due avevano il solo compito di stampare sullo Standard Output una stringa di testo passata come argomento, ma i metodi di configurazione possono essere utilizzati per svolgere funzioni anche molto complesse. In ultimo è stata fornita una serie di screenshot e listati del funzionamento, compresa la chiamata ad un undeploy dell’applicazione. Il programma progettato si è rivelato dunque in grado di soddisfare tutte le specifiche richieste in maniera semplice, efficace e veloce. La domanda di facilitare il deploy e la configurazione delle applicazioni distribuite ha avuto una risposta in termini di facilità, grazie alla configurazione via XML, ed anche in termini di estendibilità, grazie all’uso del controllo tramite JMX che permette di incorporare in maniera trasparente l’EJD anche in altre applicazioni. Le configurazioni testate in questo esempio svolgevano la funzione estremamente banale di riportare sulla riga di output una stringa di testo, ma hanno dimostrato la capacità del programma di estendersi grazie a moduli sviluppati sia come MBean remoti che come programmi Java locali. Non vi sono infatti limiti né al numero di classi o metodi chiamabili né alle funzioni che essi possono svolgere. 38 7. CONCLUSIONI L’Enterprise Java Deployer sviluppato risponde alle necessità già accennate nell’introduzione e più volte riprese all’interno del documento, ovvero il bisogno di automatizzare l’upload, il deploy e la configurazione delle parti che compongono un’applicazione java. Durante il suo sviluppo un ruolo fondamentale è stato ricoperto dalle possibilità fornite dalla tecnologia Java di management da remoto, ovvero la Java Management Extension. Questa tecnologia offre vaste possibilità di organizzare la struttura di un’applicazione sulla base di MBeans, semplici parti di programma Java esposte alla gestione remota tramite librerie e metodi standard. Tutte le operazioni principali sono infatti svolte da MBean chiamati tramite Remote Method Invocation o utilizzando la jmx-console di JBoss. La scelta di basarsi sulla JMX ha ottimizzato anche il funzionamento dell’EJD riducendo l’overhead e permettendo l’accesso diretto al core ed ai moduli fondamentali dell’Application Server JBoss. Così facendo, il controllo è più fine ed il tempo di esecuzione, controllo e configurazione è minore. Pur essendo completa, l’applicazione sviluppata in questo studio è vincolata all’utilizzo di JBoss. Un possibile sviluppo futuro potrebbe prevedere l’estensione tramite il supporto anche ad altri Application Server, come WebSphere o WebLogic. Il passaggio dovrebbe essere facilitato dal supporto che anche questi Application Server forniscono alla tecnologia JMX. Il programma progettato rappresenta inoltre un’ottima base di partenza per continuare ad estendere le sue caratteristiche, prestandosi anche a risolvere casi ancor più complessi e aprendo la strada a sviluppi futuri. Una possibile decisione potrebbe essere quella di dotare l’EJD di una capacità di auto-correggere eventuali errori al momento del deploy, fornendo la possibilità di scegliere tra strade alternative ed indicare soluzioni ai possibili problemi nati nel deploy o al momento della configurazione. In produzione i sistemi enterprise hanno bisogno di una grande affidabilità, e la possibilità di specificare vie d’uscita nel momento della nascita di un problema in parte prevedibile potrebbe aumentare la sua reliability engineering. Lungo tutto lo studio del programma, si è potuta apprezzare la grande duttilità del linguaggio Java, adatto allo sviluppo di applicazioni stand – alone ma che libera le sue vere potenzialità nella programmazione Enterprise e distribuita. Un semplice adattamento a livello implementativo di questo progetto rende comunque possibile un suo trasferimento e supporto anche di tecnologie alternative, come la piattaforma .Net di Microsoft. 39 APPENDICE A: ORDINAMENTO DI OGGETTI Una Lista l in Java può essere ordinata chiamando il metodo Collections.sort(l) [JaOrd]. Questo metodo può però essere chiamato in maniera diretta solamente in una lista composta da oggetti che implementino l’interfaccia Comparable, ovvero siano di uno dei tipi indicati in tabella A.1. Class Natural Ordering Byte Signed numerical Character Unsigned numerical Long Signed numerical Integer Signed numerical Short Signed numerical Double Signed numerical Float Signed numerical BigInteger Signed numerical BigDecimal Signed numerical Boolean Boolean.FALSE < Boolean.TRUE File System-dependent lexicographic on path name String Lexicographic Date Chronological CollationKey Locale-specific lexicographic Tabella A.1: Classi che Implementano l’interfaccia Comparable (da java.sun.com) Per altri elementi è necessario procedere in maniera differente, se si desidera un ordinamento particolare. Altrimenti, se si tenta una chiamata a Collections.sort(l) in una lista di oggetti che non implementano l’interfaccia Comparable, la JVM solleva un’eccezione di tipo ClassCastException. Nel caso della gestione delle priorità è necessario quindi modificare il codice degli oggetti che riempiono la lista in maniera tale da implementare l’interfaccia Comparable e fornire il giusto ordinamento. L’interfaccia Comparable è costituita dal codice nel listato A.1, implementa un solo metodo. public interface Comparable<T> { public int compareTo(T o); } Listato A.1: l’interfaccia Comparable 40 Il metodo compareTo confronta l’oggetto ricevuto con quello indicato e restituisce un numero: • • • Negativo nel caso in cui l’oggetto sia precedente a quello passato come argomento Zero se i due oggetti sono equivalenti Positivo se l’oggetto è successivo a quello passato come argomento Se i due oggetti non possono essere paragonati viene lanciata una ClassCastException. Nel listato A.2 si trova un esempio pratico di come viene gestita la priorità. public int compareTo(Object o) { if(getPriority() < o.getPriority()) return -1; else if(getPriority() > o.getPriority()) return 1; else return 0; } Listato A.2: il metodo compareTo di un oggetto che ha priorità ed implementa Comparable 41 APPENDICE B: TRASFORMARE UN FILE IN UN ARRAY DI BYTE Per trasferire, copiare e spostare un file può essere necessario trasformarlo in un array di byte. Nella configurazione standard un array di byte può contenere al massimo Integer.MAX_VALUE byte – ovvero 231-1 byte – ma è possibile ampliare questo limite agendo sui parametri di avvio della JVM. Da notare comunque che il limite d’origine si avvicina al GigaByte, quindi ampiamente superiore alle normali necessità. La prima operazione è la creazione di un FileInputStream del File da convertire, del quale per sicurezza si controlla che la lunghezza sia entro il limite sopra indicato. In seguito si crea un array di byte delle dimensioni del file, riempito tramite successive chiamate al metodo read dell’InputStream ed all’utilizzo di un offset impostato inizialmente a zero e crescente a seconda del numero di byte letti volta per volta. Questo funzionamento è indicato nel listato B.1. int offset = 0; int numRead = 0; while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) offset += numRead; Listato B.1: lettura di un InputStream is e scrittura su un array di byte Come controllo finale, si verifica di aver letto l’intero file tramite un confronto tra l’offset e la lunghezza del file. 42 BIBLIOGRAFIA [BauHib] [Jee] [WlsAa] [Serv] [Jsp] [Ejb] [Jta] [Jms] [Ann] [XDoc] [SunJmx] [JSR3] [JSR160] [Rmi] [Tom] [Jet] [JbDocs] [Xml] [W3c] [Xsch] [MokaXml ] [WSph] [WLog] [Dom] [Refl] [Sax] [Overl] [Ej3Tr] [HtDe] [JaOrd] [JBServ] Christian Bauer, Gavin King: “Java Persistence with Hibernate”, Manning, 2007 “Java EE at a Glance”, http://java.sun.com/javaee/ Aaron E. Walsh: “J2EE – Tutto quanto serve per apprendere funzionalità e caratteristiche fondamentali”, Mc Graw Hill, 2003 “Java Servlet Technology”, http://java.sun.com/products/servlet/ “Java Server Pages Technology”, http://java.sun.com/products/jsp/ “Enterprise JavaBeans Technology”, http://java.sun.com/products/ejb/ “Java Transaction API”, http://java.sun.com/products/jta/ “Java Message Service”, http://java.sun.com/products/jms/ Slides “J2SE 1.5 Tiger – Annotations”, Swimm Project, 27/7/2005 Craig Walls, Norman Richards: “XDoclet in Action”, Manning, 2003 “JDK 5.0 Java Management Extensions (JMX)-related APIs & Developer Guides -from Sun Microsystems:”, http://java.sun.com/j2se/1.5.0/docs/guide/jmx/index.html “JavaTM Management Extensions (JMXTM) Specification”, http://jcp.org/en/jsr/detail?id=3 “JavaTM Management Extensions (JMX) Remote API”, http://jcp.org/en/jsr/detail?id=160 “Remote Method Invocation”, http://java.sun.com/javase/technologies/core/basic/rmi/index.jsp Servlet Container “Apache Tomcat”, http://tomcat.apache.org/ “Jetty Web Server”, http://www.mortbay.org/ “Jboss Application Server Documentation Library”, http://labs.jboss.com/jbossas/docs/index.html “Extensible Markup Language (XML)”, http://www.w3.org/XML/ “World Wide Web Consortium”, http://www.w3.org/ “W3C Xml Schema”, http://www.w3.org/XML/Schema Andrea Giovannini, “Manuale Pratico di Java – Java e XML”, Hops Tecniche Nuove, 2004 “IBM Web Sphere Application Server”, http://www306.ibm.com/software/webservers/appserv/wasproductline/ “BEA WebLogic Application Server”, http://www.bea.com “Document Object Model”, http://it.wikipedia.org/wiki/Document_Object_Model Glen McCluskey, “Using Java Reflection”, Sun Developer Network, http://java.sun.com/developer/technicalArticles/ALT/Reflection/index.html “Simple Api for XML”, http://en.wikipedia.org/wiki/Simple_API_for_XML Baseline Inc., “Use method overloading in Java”, http://articles.techrepublic.com.com/5100-22-5074021.html “JBoss EJB3 TrailBlazer – JMX Service Objects”, http://articles.techrepublic.com.com/5100-22-5074021.html “Hot Deployment of Components, the URLDeploymentScanner”, The Jboss 4 Application Server J2EE Reference, http://docs.jboss.org/jbossas/guides/j2eeguide/r2/en/html/ch2.chapter.html#d0e31 04 “Object Ordering (The Java Tutorials)”, http://java.sun.com/docs/books/tutorial/collections/interfaces/order.html “JBoss Enterprise Application Platform Configuration Guide – Inside the JBoss Class Loading Architecture”, JBoss Documentation Library 43 RINGRAZIAMENTI In chiusura di tutto il lavoro, com’è giusto, arriva il momento dei ringraziamenti. Partendo da quelli dovuti, ovvero ai miei genitori, ai miei fratelli ed a tutta la mia famiglia. Un grazie talmente grande da non dover essere nemmeno più di tanto motivato. Grazie ad Elena, che in questi anni è stata la persona che più ho avuto accanto. Nello specifico, per questa tesi, devo ringraziare il Prof. Antonio Corradi, insieme all’Ing. Stefano Monti e all’Ing. Samuele Pasini, che mi hanno accompagnato nella stesura e nella progettazione del programma, aiutandomi anche a risolvere alcuni problemi incontrati per la strada. L’esperienza di questi anni di università mi ha portato a conoscere tantissime persone che hanno reso il tutto più divertente e facile. Il gruppo dei bolognesi, con Diana, Daniela, Elena, Luca, Sara e tutti gli altri con i quali ho passato diverse serate davvero memorabili. I pesaresi “in trasferta”, il grande Gianlu, Pietra, Mata e tutti gli studenti fuori sede per i quali lo studio può passare anche in secondo piano se l’alternativa è una bella uscita. Il faentino Simone (o meglio, granarolese) con il quale abbiamo costruito, nei momenti passati insieme, alcuni fantastici pezzi da “teatro dell’assurdo”. Da Imola, grande casa base, sono molte le persone che mi hanno aiutato o anche solo incoraggiato in questi anni. Amicizie che porto avanti dalle superiori (o anche prima): Valerio, Mino, Pach, Uffo, Vale, Pera, Dimitri, Cesche, Carlotta, poi mi fermo per non trasformare questa pagina in un registro scolastico. Tutti quelli che ho incontrato nell’esperienza politica, a partire da Massimo e Daniele, che mi hanno avvicinato ad un mondo grazie al quale ho incontrato Valentina, Davide, Marcello e tutti gli amici del partito e del palazzo comunale. Grazie a Canale 11, dal mitico Graziano fino a tutti i dipendenti e collaboratori con i quali è partita questa televisione, quasi dal nulla, per diventare un punto di riferimento del territorio. E con loro tutto il mondo della stampa locale e non con i quali spesso ho lavorato insieme o anche solo passato qualche serata in compagnia di una buona birra. Continuando, sarebbero davvero tantissime le persone che vorrei ringraziare e salutare. Tutti quelli che ho conosciuto, incontrato, con i quali ho anche solo scambiato due chiacchiere durante questi anni. Forse sarebbe necessario un altro documento, un allegato con solo dei ringraziamenti. Mi piacerebbe farlo, e magari prima o poi sarebbe il caso di scriverlo. Intanto mi accontento di questa pagina, sapendo quanti non ho citato e di quante altre storie vorrei parlare. Tutte le persone, e tutti i momenti, anche se non scritti qui non li ho dimenticati.