You are on page 1of 41

Riflessioni libere

Indice dei capitoli

Introduzione...............................................................................................................................................3 Strumenti comuni......................................................................................................................................3 Linguaggi...............................................................................................................................................3 Building tools........................................................................................................................................3 IDE Eclipse Indigo................................................................................................................................3 Web application server..........................................................................................................................4 Database.................................................................................................................................................4 Implementazione di Corba: Orbacus.....................................................................................................4 Costruzione di Orbacus.............................................................................................................................4 Installazione del software Orbacus di base...........................................................................................4 Realizzazione del servizio di naming....................................................................................................5 Eliminazione di ShutdownHook.......................................................................................................5 Terminazione guidata del servizio....................................................................................................6 Primo utilizzo di NameService Console...........................................................................................6 Assegnazione del numero di porta....................................................................................................7 Utilizzo del servizio di naming in modalit persistente...................................................................9 Considerazioni generali sulla sovrascrittura di rt.jar............................................................................9 Ciclo di sviluppo di oggetti Corba, server e client puri..........................................................................10 Impostazione generale della libreria MyCorbaWorld........................................................................11 Definizione delle interfacce IDL e mapping su Java..........................................................................11 Implementazione degli oggetti server.................................................................................................12 Classi di utilit generale......................................................................................................................13 Descrizione della classe GestoreCorba...........................................................................................13 Descrizione della classe CorbaThread............................................................................................18 Applicazione host prototipale: GenericCorbaHost............................................................................19 Inizializzazione................................................................................................................................19 Funzionamento a regime.................................................................................................................21 Terminazione...................................................................................................................................21 Build................................................................................................................................................21 Esecuzione.......................................................................................................................................22 Importante variazione del funzionamento nominale di un host.....................................................22 Applicazione client prototipale: HelloClient......................................................................................22 Note sulloggetto ServerHello........................................................................................................22 Inizializzazione................................................................................................................................23 Funzionamento a regime.................................................................................................................24 Terminazione...................................................................................................................................24 Build ed esecuzione.........................................................................................................................25 Considerazioni sul ciclo di vita degli oggetti Corba...........................................................................25 Modifiche ad hoc alla libreria MyCorbaWorld..............................................................................25 Modifiche a HelloClient..................................................................................................................27 Modifiche a GenericCorbaHost......................................................................................................27 Scenario generale di funzionamento...............................................................................................27 Sintesi conclusiva............................................................................................................................30 Costruzione della Axis2 web service LibreriaWS..................................................................................30 Impostazione del progetto Eclipse LibreriaWS..................................................................................30 Fase di build....................................................................................................................................30 Installazione.....................................................................................................................................30 WSDL, linterfaccia verso i client..................................................................................................31 Realizzazione di un client di LibreriaWS...........................................................................................31 Impostazione del progetto client: LibreriaWSClient......................................................................31 1/41

Riflessioni libere Costruzione della jax-ws web service LibreriaWSNew.........................................................................33 Preparazione del framework jax-ws....................................................................................................33 Impostazione del progetto Eclipse LibreriaWSNew..........................................................................34 Fase di annotation processing.........................................................................................................34 Fase di creazione del WAR file......................................................................................................35 Installazione.....................................................................................................................................36 Impostazione del progetto Eclipse LibreriaWSNewClient................................................................36 Fase di generazione dello stub........................................................................................................36 1.1.2.Utilizzo dello stub nel codice client.......................................................................................37 Gestione degli eventi in Corba................................................................................................................39 Generalit su oggetti pusher e puller in Corba...................................................................................39 Lapplicazione Event Service di Orbacus...........................................................................................39 Funzionamento di applicazioni pusher e puller astratte.....................................................................40 Visione dinsieme della mia architettura............................................................................................40 Alcune ipotesi restrittive.................................................................................................................40 Descrizione di unapplicazione pusher: PusherPeopleApp............................................................41

2/41

Riflessioni libere

Introduzione.
Questo documento descrive lo sviluppo della mia applicazione Fenester, con tutti i suoi annessi e connessi. Fenester unapplicazione desktop, interattiva, sviluppata utilizzando Java SWT; essa interagisce con la axis2 web service LibreriaWS, e con loggetto Corba ServerPeople (istanziato da GenericCorbaHost).

Strumenti comuni.
Ho lavorato sempre in Windows, 32 bit, XP Professional oppure 7 Professional, in italiano.

Linguaggi.
Il linguaggio principalmente utilizzato Java, versione 1.7.0_02; lho installato tramite il pacchetto di installazione jdk-7u2-windows-i586.exe, usando le scelte default: cos facendo, Java risulta installato nella directory C:\Program Files\Java\jdk1.7.0_02. Ho definito la variabile dambiente, di sistema, JAVA_HOME = C:\Program Files\Java\jdk1.7.0_02; poi ho aggiunto %JAVA_HOME%\bin alla variabile dambiente Path.

Building tools.
Come strumento indipendente, non compreso in altri pacchetti (cio not embedded), ho installato Apache ant, versione 1.7.1. Prima ho scaricato apache-ant-1.7.1-bin.zip, quindi ho unzippato sotto C:\, cos stata creata la directory C:\apache-ant-1.7.1 con le sue sottodirectory. Poi ho definito la variabile dambiente, di sistema, ANT_HOME = C:\apache-ant-1.7.1, e ho aggiunto %ANT_HOME%\bin alla variabile dambiente Path. Notare: la versione successiva, ant 1.8.2, non mi permette di compilare correttamente Orbacus mantenendo Java 1.7; il problema spiegato nel seguito.

IDE Eclipse Indigo.


Ho usato Eclipse Indigo (Eclipse 3.7.1), nella versione for Java developers. Bisogna scaricare il pacchetto eclipse-java-indigo-win32.zip, quindi unzippare sotto C:\, cos viene creata la directory C:\eclipse con alcune sottodirectory. Tuttavia servono alcuni accorgimenti, per poter operare come desiderato. Prima di tutto bisogna modificare il file eclipse.ini, da un certo punto in poi: eclipse.ini originale eclipse.ini modificato ... ... --launcher.XXMaxPermSize --launcher.XXMaxPermSize 256m 256m --launcher.defaultAction -vm openFile C:/Program Files/Java/jdk1.7.0_02/bin/javaw.exe -vmargs openFile -Dosgi.requiredJavaVersion=1.5 -vmargs -Xms40m -Dosgi.requiredJavaVersion=1.7 -Xmx384m -Xms40m -Xmx512m Per comodit, definisco la variabile dambiente, di sistema, ECLIPSE_HOME = C:\eclipse. Una volta installato Eclipse, scelgo Help Install new software e controllo What is already installed ? Trovo che lunico plugin installato Eclipse IDE for Java Developers. Volendo utilizzare SWT, bisogna installare i plugin appositi. Per farlo, scelgo HelpInstall new software, come prima, Work with = All available sites, e dalla lista che si presenta scelgo General purpose tools, di qui seleziono SWT Designer e SWT Designer Core, e li installo1.
1

A differenza di SWT Designer, WindowBuilder e Swing Designer sono gi installati, poich fanno parte di Eclipse IDE for Java Developers.

3/41

Riflessioni libere
Con ci, Eclipse Indigo pronto per sviluppare applicazioni SWT. Notare: per poter utilizzare JDK 1.7, bisogna installare Eclipse Indigo SR1.

Web application server.


Viene utilizzato Apache Tomcat 7.0, corredato di axis2 come libreria di implementazione delle web service. Ho installato Tomcat usando il pacchetto apache-tomcat-7.0.22.exe; ho seguito tutti i default, tranne che per la porta principale, al posto del default 8080 ho assegnato 8081. Per installare Apache axis2, ho scaricato il pacchetto axis2-1.6.1-bin.zip ( la standard binary distribution, che non necessita compilazione), e ho unzippato sotto C:\, ottenendo cos la directory C:\axis2-1.6.1. Ho definito la variabile dambiente, di sistema, AXIS2_HOME = C:\axis2-1.6.1. Per costruire axis2.war, apro un command prompt e mi posiziono in <AXIS2_HOME>\webapp, ove si trova build.xml, e do il comando ant create.war. Cos facendo, viene creato axis2.war entro la directory <AXIS2_HOME>\dist. Copio il file axis2.war entro la directory webapps di Tomcat, con Tomcat running: automaticamente Tomcat crea la directory axis2 entro webapps.

Database.
Viene utilizzato Oracle Express 10g. Dopo linstallazione, il TNS name XE. Sono presenti due utenti/database: Libreria e DBPeople. Ho creato la directory Drivers entro oraclexe, contenente ojdbc6.jar, usato per laccesso JDBC dal linguaggio Java, JDK 1.6 o superiore.

Implementazione di Corba: Orbacus.


Come software di implementazione di Corba, viene usato Orbacus, versione 4.3.4 per Java. Orbacus tuttavia non un prodotto finito, che va semplicemente installato; bisogna invece costruirlo, e il procedimento, piuttosto complesso, descritto in un apposito capitolo.

Costruzione di Orbacus.
La costruzione di Orbacus pu essere articolata in due fasi: installazione del software e realizzazione del servizio di naming. Infatti Orbacus non fornisce un programma di naming compiuto, fornisce solo i componenti necessari alla sua costruzione; quindi, una volta installato il software Orbacus di base (prima fase), se si vuole disporre di un servizio di naming necessario compiere la seconda fase.

Installazione del software Orbacus di base.


Bisogna scaricare JOB-4.3.4-win32.zip e fare il suo unzip: vengono cos create due directory, JOB-4.3.4 e JOB-4.3.4-bin; la prima contiene tutti i sorgenti del prodotto e i makefile, la seconda alcuni eseguibili. Creo C:\Orbacus, vuota, poi creo la sua sottodirectory bin e vi copio tutti i file presenti in JOB-4.3.4-bin\bin; fatto ci, posso anche cancellare la directory JOB-4.3.4-bin. Poi aggiungo C:\Orbacus\bin al path. Anche se non strettamente necessario, allinterno di JOB-4.3.4\imports.xml nel tag <javac> aggiungo lattributo includeantruntime = false. Apro un command prompt, mi posiziono nella directory JOB-4.3.4 e do il comando di build: ant Dinstall.dir=C:\Orbacus. Usando ant 1.7.1, non ci sono problemi con XP n con Windows7; invece usando ant 1.8.2 si verificano errori con entrambi i sistemi operativi. Mettere spiegazione Nella directory Orbacus sono comparse due sottodirectory nuove: idl, che contiene (entro la sottodirectory OB) i file IDL predefiniti; lib, contenente i file JAR, cio le librerie di Orbacus.

4/41

Riflessioni libere
possibile scrivere programmi che usano Corba attraverso file IOR; tuttavia in genere non si vuole lavorare coi file IOR, ma si preferisce utilizzare un servizio di naming,

Realizzazione del servizio di naming.


La documentazione Orbacus suggerisce solamente luso della classe com.ooc.CosNaming.Server con svariate opzioni, ma il comando (da command prompt) java com.ooc.CosNaming.Server causa un errore: impossibile trovare o caricare la classe principale com.ooc.CosNaming.Server. Questo problema si verifica perch il comando accede le classi del runtime Java omonime delle classi Orbacus, e si risolve imponendo ladeguata opzione Xbootclasspath alla JVM: java Xbootclasspath/p:C:\Orbacus\lib\OB.jar;C:\Orbacus\lib\OBNaming.jar com.ooc.CosNaming.Server Per semplicit, scrivo questo comando come server.bat:
java Xbootclasspath/p:C:\Orbacus\lib\OB.jar;C:\Orbacus\lib\OBNaming.jar com.ooc.CosNaming.Server %1%2 %3 %4

i parametri di passaggio sono necessari per il passaggio di opzioni, ad esempio server v2. Lerrore non si verifica pi: lanciando server.bat, il processo sta in piedi; tuttavia ci sono molti problemi. Questi verranno risolti con modifiche progressive al codice.

Eliminazione di ShutdownHook.
Il cattivo comportamento pi evidente il seguente: il passaggio di opzioni semplici come h (help) o v (versione) ottiene la risposta attesa, ma il servizio termina con un errore:

Quanto segue riferito al codice di C:\JOB-4.3.4\naming\src\com\ooc\CosNaming\Server.java. Scegliendo h e v, il programma risponde e non attiva loggetto orb di tipo ORB, ossia non giunge a compiere orb.run, che blocca il thread corrente; passando solo server, oppure server con altre opzioni, compie la chiamata bloccante. Nei casi di h e v, tenta di uscire subito, quindi invoca la orb.destroy: questa va a buon fine, ma il suo compimento causa lesecuzione del codice del metodo run delloggetto shutdownHook_ di tipo ShutdownHook: il codice del metodo ShutdownHook.run tenta di invocare il metodo shutdown sulloggetto ORB dopo che questo stato distrutto, di qui lerrore ! chiaro che lerrore non riguarda solo le opzioni h e v, semplicemente negli altri casi non me ne accorgo perch il programma resta bloccato e lerrore si verificherebbe solo alla sua uscita. Ho scelto la strada pi semplice: ho eliminato tutto il codice inerente a ShutdownHook entro Server.java, e ho pure eliminato il file ShutdownHook.java. Notare: mi basta rigenerare il solo OBNaming.jar, per farlo posso posizionarmi in JOB-4.3.4\naming e dare il comando ant Dinstall.dir=C:\Orbacus in; questa scorciatoia funziona solo se ho fatto almeno una volta la compilazione completa e non ho poi fatto ant clean. Cos facendo, il malfunzionamento delle opzioni h e v risolto.

In seguito, sar chiaro perch ho previsto 5 parametri.

5/41

Riflessioni libere

Terminazione guidata del servizio.


Il servizio di naming dovrebbe poter terminare in modo dolce, non abortendo, quindi evitando il ^C o la chiusura diretta del command prompt ospite. Ho implementato un sistema semplice: prima di entrare nel ciclo infinito della orb.run, il programma setta un timer e ogni tot tempo controlla se si verificata la condizione di uscita; come condizione duscita ho scelto lesistenza di un file, DeathNS.dat, entro la directory che ospita la libreria OBNaming.jar, quindi in C:\Orbacus\lib. Nel codice di Server.java, sostituisco alla sola istruzione orb.run il codice seguente: java.util.Timer tmr = new java.util.Timer(); Taskent task = new Taskent(orb); tmr.schedule(task,10000,10000); orb.run(); tmr.cancel(); ogni volta che scade il timer (10 secondi), e dopo un delay iniziale di 10 secondi, viene eseguito il metodo run delloggetto task di tipo Taskent, la mia classe applicativa che deve essere subtipo di TimerTask. Ecco il codice di Taskent: final class Taskent extends java.util.TimerTask { private org.omg.CORBA.ORB orb; public Taskent(org.omg.CORBA.ORB orb) { java.io.File f = new java.io.File("DeathNS.dat"); if (f.exists()) f.delete(); this.orb = orb; } @Override public void run() { java.io.File f = new java.io.File("DeathNS.dat"); if (f.exists()) { f.delete(); orb.shutdown(true); } } } il codice di Server.java crea listanza task della classe Taskent e le passa loggetto ORB attivo; il costruttore di Taskent salva il riferimento allORB, verifica se esiste gi il file DeathNS.dat e se s lo cancella (se esistesse, si tratterebbe di un residuato). Poi il codice di Server fa partire, con un ritardo iniziale, il timer: ogni 10 secondi verr attivato il metodo Taskent.run, che opera in un thread separato da quello principale, che bloccato dopo la chiamata orb.run: il metodo Taskent.run verifica se il file DeathNS.dat esiste, se s elimina il file e invoca orb.shutdown. Ci determina lo sblocco del thread principale e quindi lesecuzione di orb.destroy, stavolta nellordine giusto, quindi il programma termina correttamente. Perci per terminare correttamente il servizio di naming sufficiente creare il file Death.dat nella directory C:\Orbacus\lib; per semplicit, ho previsto StopNameService.bat, che crea il file di terminazione.

Primo utilizzo di NameService Console.


Orbacus fornisce unapplicazione capace di interagire graficamente con il servizio di naming: la cosiddetta NameService Console; secondo la documentazione, la console pu essere lanciata da command prompt, posizionato in C:\Orbacus\lib (dato che anche le classi che implementano la console si trovano in OBNaming.jar): java com.ooc.CosNamingConsole.Main, corredabile con varie opzioni. Al solito, in questo modo c errore, perch bisogna utilizzare lopzione Xbootclasspath: java Xbootclasspath/p:C:\Orbacus\lib\OB.jar Xbootclasspath/p:C:\Orbacus\lib\OBNaming.jar Xbootclasspath/p:C:\Orbacus\lib\OBUtil.jar com.ooc.CosNamingConsole.Main

6/41

Riflessioni libere
che scrivo (con laggiunta finale di un paio di parametri, %1 e %2) come console.bat. Supponendo di aver lanciato il servizio di naming con il comando server, lancio la console tramite il comando console: la console non riesce per a trovare il servizio di naming. C una via euristica, basata sullimpiego di un IOR file. La classe Server prevede, tra le varie opzioni, lopzione i che mostra lo IOR, cio lo stringified object key, sullo standard output; allora voglio che lo IOR venga scritto in un file: server i > ns.ior. In questa forma per, il file IOR generato, ns.ior, non va bene, perch ci finisce dentro anche il comando; allora scrivo il seguente batch file, muteserver.bat: echo off call server -i > ns.ior echo on in questo modo il servizio di naming attivo e pubblica il proprio IOR nel file ns.ior. In un altro command prompt, lancio il comando console f ns.ior, con questa opzione la console accede al servizio di naming usando lo IOR passato nel file: funziona correttamente. Ecco ci che viene visualizzato:

Non esamino qui le funzionalit della console, mi limito a due osservazioni: a) il root namespace si chiama Root, ogni namespace applicativo viene creato in esso b) in basso, la console dice che il servizio di naming a cui connessa in esecuzione su un certo host e utilizza una certa porta, nel caso presente la porta 57261.

Assegnazione del numero di porta.


Come pu unapplicazione collegarsi al servizio di naming per accedere ai servizi da esso offerti, cio registrazione di oggetti Corba da parte di applicazioni server e connessione a tali oggetti da parte delle applicazioni client, o altre operazioni come la creazione di un namespace applicativo, etc ?

7/41

Riflessioni libere

La via pi semplice ed elegante luso di corbaloc, ed quella che perseguir, tuttavia questa via possibile a patto che loggetto da localizzare usi una porta nota a chi vuole accederlo. Tuttavia questo non il caso del servizio di naming fornito da Orbacus: ogni volta che viene attivato, il servizio utilizza una porta diversa, scelta randomicamente. Suppongo di attivare il servizio usando muteserver.bat; poi lancio la console, usando lo IOR file, e scopro che il servizio usa la porta 57261. A questo punto posso lanciare una seconda istanza di console senza pi bisogno di usare il file IOR, ma usando corbaloc; in un altro command prompt, do il comando seguente: console ORBInitRef NameService=corbaloc::localhost:57261/NameService Anche la seconda console funziona perfettamente ! Una soluzione potrebbe essere quella di far pubblicare al servizio la propria porta, permettendo cos ai client di accederlo via corbaloc. Preferisco per unaltra soluzione: avere la possibilit di lanciare il servizio su una porta precisata alla partenza. Allo scopo, modifico il servizio in modo che accetti lopzione p seguita da una stringa numerica, cos potr scrivere server p n ed essere sicuro che il servizio attivato user la porta n; inoltre definisco comunque una porta default (la 11000) da utilizzare se non viene specificata lopzione p. Entro Server.java, modifico la routine usage affinch visualizzi lopzione p e le sue caratteristiche duso. Gestisco lopzione p diversamente dalle altre: anzich in run,la gestisco nel main, non molto belloecco il codice nel main: java.util.Properties props = System.getProperties(); // le due righe seguenti sono preesistenti; non servono: vedere in seguito props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB"); props.put("org.omg.CORBA.ORBSingletonClass","com.ooc.CORBA.ORBSingleton"); int door = 11000; for(int i = 0 ; i < args.length && args[i].startsWith("-") ; i++) { if(args[i].equals("--port") || args[i].equals("-p")) { ++i; if(i >= args.length) { System.err.println(progName + ": " + args[i - 1] + "expects argument"); usage(progName); System.exit(1); } try { door = Integer.parseInt(args[i]); if (door <= 0) throw new NumberFormatException(); } catch (NumberFormatException exc) { System.err.println(progName + ": " + args[i - 1] + " has a wrong argument (not a positive number)"); usage(progName); System.exit(1); } break; } } props.put("ooc.orb.oa.endpoint", "iiop --port "+String.valueOf(door)); System.setProperties(props);

8/41

Riflessioni libere
infine modifico la routine run affinch non dia errore trovando lopzione p. Adesso posso lanciare il servizio di naming sulla porta che preferisco, ad es server p 12000, e se dimentico la porta so che viene usata non una porta a caso ma la porta 11000. Riscrivo console.bat: setlocal if not "%1" == "" set /a door = %1 if "%1" == "" set /a door = 11000 java -Xbootclasspath/p:"C:\Orbacus\lib\OB.jar" -Xbootclasspath/p:"C:\Orbacus\lib\OBNaming.jar" -Xbootclasspath/p:"C:\Orbacus\lib\OBUtil.jar" com.ooc.CosNamingConsole.Main -ORBInitRef NameService=corbaloc::localhost:%door%/NameService endlocal Cos se lancio server p 12000, poi lancio console 12000; se lancio solo server, allora dallaltra parte devo lanciare solo console. Tutto funziona correttamente.

Utilizzo del servizio di naming in modalit persistente.


Le modifiche al software Orbacus, e la preparazione degli opportuni command file, sono complete; quanto segue la descrizione di un modus operandi attuato in base al software prodotto. Suppongo di attivare il servizio di naming e poi la console, con questultima creo un namespace, VirgoSpace, entro Root:

Poi termino il servizio. Quando lo rilancio, non trovo pi traccia del namespace creato: gli oggetti non sono persistenti. In generale, voglio che il servizio di naming sia persistente. Posso farlo utilizzando le opzioni giuste: impongo al servizio di creare un object store, che utilizzer ad ogni successiva riattivazione. Chiamo ObjStore.dat lobject store, e lo creo entro C:\Orbacus\lib, tramite il seguente comando: server p 12000 s d ObjStore.dat3, attiva il servizio sulla porta 12000 e impone la creazione e lutilizzo dellobject store ObjStore.dat. Lopzione s ha senso solo se seguita da d, e lobject store non deve gi esistere (altrimenti luso di s d errore). In unaltra finestra attivo la console, console 12000, e creo in Root il namespace VirgoSpace; poi esco dalla console e termino il servizio. Rilancio il servizio imponendogli di usare lobject store: server p 12000 d ObjStore.dat, la porta devessere la stessa altrimenti si verifica un errore runtime; lancio la console: console 12000, stavolta ritrovo il namespace VirgoSpace, c la persistenza. Nello studio dellapplicazione Fenester, uso il servizio di naming in modalit persistente sulla porta 12000.

Considerazioni generali sulla sovrascrittura di rt.jar.


La documentazione Orbacus si raccomanda di usare il seguente codice prima di inizializzare loggetto ORB:
3

Si noti che per passare il comando servono 5 parametri; la forma pi complessa tra quelle che utilizzo comunemente.

9/41

Riflessioni libere
java.util.Properties props = System.getProperties(); props.put("org.omg.CORBA.ORBClass", "com.ooc.CORBA.ORB"); props.put("org.omg.CORBA.ORBSingletonClass","com.ooc.CORBA.ORBSingleton"); System.setProperties(props); tuttavia la sostituzione di queste sole classi in generale del tutto insufficiente, e si rende necessaria lopzione Xbootclasspath almeno su OB.jar come passaggio alla JVM. Ma, dato che OB.jar viene comunque usato nel runtime al posto delle classi omonime di rt.jar, e dato che le classi ORB e ORBSingleton appartengono a OB.jar, allora il codice di cui sopra inutile, verranno comunque adottate le classi giuste. In effetti, dopo la rimozione delle due righe in causa da Server.java, il servizio continua a funzionare invariato; nel seguito, ometter le due righe nei file da me codificati. Presa una applicazione Corba, sia client sia host, implementata in App.jar, in generale prevedo un file di lancio launchApp.bat cos fatto (a meno di parametri di passaggio sulla command line): java -Xbootclasspath/p:"C:\Orbacus\lib\OB.jar;C:\Orbacus\lib\OBNaming.jar" -jar App.jar OB.jar assolutamente necessario al funzionamento dellapplicazione; invece pu essere omesso OBNaming.jar, perch le classi appartenenti a questultimo, come NamingContext e NamingContextExt (e i relativi helper), differiscono poco dalle omologhe classi di rt.jar. Suppongo che nella applicazione Corba App sia definita la variabile vs di tipo NamingContext, e le sia assegnato il riferimento al contesto applicativo VirgoSpace; poi viene eseguito il seguente codice, ove Info un array di stringhe: Class c = vs.getClass(); Package p = vs.getClass().getPackage(); Info[0] = "VirgoSpace: Class="+c.getName(); Info[1] = "Package(VirgoSpace): Title="+p.getImplementationTitle(); Info[2] = "Package(VirgoSpace): Vendor="+p.getImplementationVendor(); Info[3] = "Package(VirgoSpace): Version="+p.getImplementationVersion()); Suppongo di eseguire App utilizzando il seguente comando: java -Xbootclasspath/p:"C:\Orbacus\lib\OB.jar;C:\Orbacus\lib\OBNaming.jar" -jar App.jar ecco cosa trovo in Info: Info[0] = "VirgoSpace: Class=org.omg.CosNaming._NamingContextStub" Info[1] = "Package(VirgoSpace): Title=null" Info[2] = "Package(VirgoSpace): Vendor=null" Info[3] = "Package(VirgoSpace): Version=null" Adesso suppongo di eseguire App utilizzando il seguente comando: java -Xbootclasspath/p:"C:\Orbacus\lib\OB.jar" -jar App.jar ecco cosa trovo in Info: Info[0] = "VirgoSpace: Class=org.omg.CosNaming._NamingContextStub" Info[1] = "Package(VirgoSpace): Title=Java Runtime Environment" Info[2] = "Package(VirgoSpace): Vendor=Oracle Corporation" Info[3] = "Package(VirgoSpace): Version=1.7.0_02" Nel primo caso vengono usati i tipi di OBNaming.jar, che non definiscono alcun marchio di fabbrica sul package org.omg.CosNaming, mentre nel secondo caso i tipi di rt.jar, che invece definiscono il marchio di fabbrica sul package. Nonostante lalto grado di compatibilit, che consente il corretto funzionamento della maggior parte delle applicazioni, si preferisce assegnare anche OBNaming.jar al boot classpath.

Ciclo di sviluppo di oggetti Corba, server e client puri.


Nel suo scenario pi semplice, Corba prevede oggetti server e oggetti client; ci sono scenari pi complessi, cio un oggetto che contemporaneamente server (verso alcuni oggetti) e client (verso altri oggetti, o anche verso gli stessi dei quali server: gli oggetti A e B possono essere reciprocamente server e client).

10/41

Riflessioni libere
Qui mi limiter a studiare il caso di oggetti semplici: o server puri o client puri. Loggetto ServerPeople un Corba server, cio un oggetto compliant agli standard Corba che espone metodi richiamabili da altri oggetti detti Corba client. Per estensione, spesso dico che unapplicazione (sia essa unapplicazione desktop o console, oppure un applet, o una pagina JSP etc) client Corba quando ha allinterno uno o pi oggetti che sono client Corba; analogamente, unapplicazione detta server Corba quando contiene uno o pi oggetti server Corba. In particolare, unapplicazione che istanzia un oggetto server Corba, mettendolo a disposizione dei client, detta essere un host delloggetto server Corba. Lapplicazione Fenester Corba client, in quanto accede ai metodi delloggetto ServerPeople, che un server Corba. Loggetto ServerPeople viene istanziato e reso accessibile dallapplicazione desktop GenericCorbaHost, che quindi lhost. La realizzazione degli oggetti server e client Corba puri pu essere riassunta in una sequenza di fasi. Non c una sola sequenza di fasi, io mi limiter a seguire ed esporre quella a mio avviso pi adatta agli scopi. La sequenza seguita la seguente: a) definizione dellinterfaccia client server (ossia il file IDL) b) implementazione degli oggetti server c) classi che implementano funzionalit frequenti e generali d) realizzazione dellapplicazione host delloggetto server e) utilizzo delloggetto server da parte di applicazioni client. Concretamente, tutto verr svolto utilizzando Eclipse.

Impostazione generale della libreria MyCorbaWorld.


La soluzione da me fornita prevede la costruzione di un unico applicativo per realizzare le prime tre fasi: usando Eclipse Indigo, ho creato il Java project MyCorbaWorld; suo scopo la realizzazione della libreria MyCorbaWorld.jar (entro la directory LibJar). Questa libreria contiene tre tipi di oggetti, distribuiti in tre distinti folder di progetto: i) la sottodirectory IDL, contenente i file IDL che definiscono le interfacce degli oggetti server ii) il package srvCorba, contenente le implementazioni degli oggetti server iii) il package utiCorba, che contiene classi atte a svolgere il lavoro sempre pi o meno uguale necessario per istanziare ed esporre gli oggetti server da parte delle applicazioni host, nonch le operazioni che i client devono compiere per connettersi agli oggetti server. Lo scopo della libreria MyCorbaWorld far s che le applicazioni, sia client sia server, possano rapportarsi unicamente con la libreria, senza bisogno di interagire direttamente con il middleware Corba, a patto di seguire certe regole che saranno dettagliate nel seguito, Una via semplice per rendere ci possibile assumere che la libreria MyCorbaWorld contenga tutti gli oggetti server sviluppati. Ci a sua volta implica che il progetto della libreria dovr contenere (entro il folder IDL) tutte le interfacce IDL. Fornir spiegazioni generali, e spiegazioni particolareggiate per gli oggetti dinteresse nel case study (cio, tutto quanto attinente allapplicazione Fenester). Verr data molta importanza al file build.xml.

Definizione delle interfacce IDL e mapping su Java.


La sottodirectory IDL contiene tutte le interfacce disponibili; attualmente sono presenti People.idl, Sistema.idl, Hello.idl; lunica attinente a Fenester People.idl. Segue il codice di People.idl: module persons { struct Person { long Matricola; string Nome; string Cognome; string CodiceFiscale; };

11/41

Riflessioni libere
typedef sequence<Person> Group; interface People { Group PeopleListBySurname(in string cgn); Group PeopleListByRange(in long mi,in long mf); };

}; invece Hello.idl esprime linterfaccia Hello entro il modulo greetings, Sistema.idl esprime linterfaccia Sistema entro il modulo sys; altri oggetti server potranno aggiungersi nel seguito. Il linguaggio IDL permette la definizione di tipi, e nientaltro. Un costrutto come il seguente: module persons { long Matricola; ... sbagliato, perch qui Matricola sarebbe una variabile e non un tipo. I tipi struct sono un insieme di campi senza metodi, per i tipi interface vale il contrario. La keyword typedef funge come ridefinizione di tipo, mentre lespressione sequence<X> rappresenta un array di oggetti di tipo X. Il mapping delle interfacce IDL su tipi Java viene compiuto dallutility jidl, che si trova in Orbacus\bin, ed attivata dal target <idlGen> di build.xml: <target name="idlGen" description="jidl activation"> <exec executable="${Orbacus.tools}/jidl"> <arg line="--output-dir src"/> <arg line="--all"/>4 <arg value="IDL/*.idl"/> </exec> </target> ove Orbacus.tools indirizza Orbacus\bin. Per ciascun differente (nome di) modulo, viene generato un package omonimo, quindi in src compaiono i tre package greetings, persons e sys. Dal file People.idl per effetto della compilazione vengono generati i seguenti tipi, tutti appartenenti al package persons: a) Group.helper, Person.helper, People.helper una classe helper viene generata per qualsiasi tipo presente nel file IDL; se il tipo X una ridefinizione semplice di un tipo Y, cio typedef X Y, per quanto concerne Y viene generata la sola classe X.helper b) Group.holder, Person.holder, People.holder una classe holder viene generata per qualsiasi tipo presente nellIDL, a meno che il tipo non sia una ridefinizione semplice; in altre parole, typedef X Y non genera Y.holder, mentre typedef sequence<X> Y porta alla generazione di Y.holder c) Person.java, People.java una classe omonima viene generata per ogni tipo struct oppure interface presente nellIDL; se il tipo IDL una struct, come Person, allora Person.java una classe, mentre se il tipo IDL una interface, come People, allora People.java una java interface d) _PeopleStub.java, PeoplePOA.java, PeopleOperations.java le classi stub, POA e Operations vengono generate se e solo se il tipo IDL una interface. Quanto detto per People.idl, facilmente trasponibile per tutti gli altri file IDL.

Implementazione degli oggetti server.


Studio il caso delloggetto ServerPeople, ma quanto dico trasponibile anche per gli altri oggetti server. La classe ServerPeople deve implementare People.idl, quindi la creo come subtipo di PeoplePOA; essendo un oggetto server, la creo entro il package srvCorba. Ecco come si presenta la classe ServerPeople appena creata (avendo avuto cura di richiedere Inherit abstract methods): package srvCorba; import persons.PeoplePOA;
4

Questa riga necessaria per poter compilare i file IDL che ne includono altri.

12/41

Riflessioni libere
import persons.Person; public class ServerPeople extends PeoplePOA { @Override public Person[] PeopleListBySurname(String cgn) { return null; } @Override public Person[] PeopleListByRange(int mi, int mf) { return null; } } Si noti che il tipo IDL Group, sequence<Person>, mappato su un comune array Java, Person[]. Limplementazione dei metodi semplice. Considero limplementazione di PeopleListByRange: apro una connessione al database Oracle DBPeople e scopro quanti record della table PEOPLE hanno matricola compresa tra mi e mf, quindi alloco un array di tipo Person[], lo riempio coi dati trovati e, dopo aver chiuso la connessione al database, lo restituisco al chiamante. Se non ci sono record che soddisfano alla condizione, restituisco un array vuoto (di lunghezza zero); se si verificano invece errori nellaccesso al database, ritorno null. Limplementazione degli altri oggetti server (ServerSistema, ServerHello) del tutto analoga.

Classi di utilit generale.


Queste classi appartengono al package utiCorba; al momento, sono definite le seguenti classi: a) GestoreCorba.java b) CorbaThread.java. La prima una classe ricca, svolge la maggior parte del lavoro ed lunica con cui si interfacciano le applicazioni, sia client, sia server (ossia applicazioni host); la seconda un piccolo bean che svolge un unico compito, ben definito ma critico.

Descrizione della classe GestoreCorba.


La descrizione fornita da un punto di vista essenzialmente funzionale: cosa deve fare unapplicazione host per istanziare ed esporre un oggetto, cosa deve fare unapplicazione client per chiamare i metodi delloggetto server. Si rende quindi necessario anticipare alcune informazioni sulle applicazioni server e client.

1.1.1.1. Funzionamento di unapplicazione host e di unapplicazione client astratte. Assumo che lapplicazione host sia di tipo desktop, che possa istanziare ed esporre uno o pi oggetti server, e che utilizzi il servizio di naming per esporre gli oggetti server, registrandoli in un namespace (o contesto) gi esistente e definito.
In particolare, ipotizzo che il servizio di naming sia attivo su una macchina nota (tramite nome DNS o indirizzo IP) e su una porta assegnata, anchessa nota. Ipotizzo anche che il contesto utilizzato dagli oggetti server e dalle applicazioni client sia uno solo, noto, cio VirgoSpace (creato a suo tempo usando la console del servizio di naming). Quindi, lapplicazione host deve poter svolgere le seguenti operazioni: 1) istanziare gli oggetti ORB e POA 2) connettersi al servizio di naming 3) risolvere il contesto in cui risiedono gli oggetti server 4) istanziare gli oggetti server 5) esporre gli oggetti istanziati nel contesto del servizio di naming prima risolto 6) attivare gli oggetti server, in modo che siano effettivamente disponibili a servire le chiamate delle applicazioni client

13/41

Riflessioni libere 7) deallocare gli oggetti server dal contesto, quando lapplicazione host cessa 8) terminare correttamente loggetto ORB, quando lapplicazione host cessa.
Per quanto riguarda le applicazioni client, ipotizzo che esse utilizzino lo stesso servizio di naming e cerchino gli stessi oggetti server entro lo stesso contesto applicativo. Lapplicazione client deve poter svolgere le seguenti operazioni: I) istanziare loggetto ORB II) connettersi al servizio di naming III) risolvere il contesto in cui risiedono gli oggetti server IV) agganciare gli oggetti server solo quelli desiderati ! V) richiamare i metodi degli oggetti server VI) terminare correttamente loggetto ORB, quando lapplicazione client cessa. Come ipotesi semplificativa, suppongo che le applicazioni client trovino gi attivi gli oggetti di cui abbisognano, quindi che lhost preposto sia in esecuzione. Le applicazioni, sia host sia client, devono poter compiere le operazioni suddette interfacciandosi con la libreria MyCorbaWorld, e nientaltro; e linterfacciamento devessere di alto livello, cio host e client non devono preoccuparsi dei dettagli Corba. La grande maggioranza del lavoro viene svolto dalla classe GestoreCorba, con lausilio della classe CorbaThread nel solo caso di applicazioni host ma CorbaThread invisibile allhost. Nel seguito, spiegher come la classe GestoreCorba pu effettivamente offrire a host e client i suoi servizi.

1.1.1.2. Istanziamento delloggetto ORB. La classe GestoreCorba mette a disposizione il metodo InitStuff: public boolean InitStuff(boolean isServerApp,String NShost,int NSdoor)
La classe ha il membro (privato) orb, che viene istanziato tramite ORB.init; utilizzo il meccanismo delle Java Properties per far s che in seguito loggetto ORB possa risolvere il servizio di naming mediante il meccanismo corbaloc (standard Corba):
Properties props = System.getProperties(); props.put("ooc.orb.service.NameService","corbaloc::"+NShost+":"+NSdoor+"/NameService"); System.setProperties(props); orb = org.omg.CORBA.ORB.init(new String[]{}, props);

Unaltra possibilit era lutilizzo dellopzione ORBconfig, con la quale passo allistanza di ORB un file di configurazione, NSAddr.dat: String[] Args = {"-ORBconfig","C:\\Orbacus\\lib\\NSAddr.dat"}; orb = org.omg.CORBA.ORB.init(Args, null); ove NSAddr.dat ha il testo seguente:

# Initial services ooc.orb.service.NameService=corbaloc::<NShost>:<NSdoor>/NameService


Utilizzo il primo meccanismo, perch mi consente di risparmiare un file esterno. Le applicazioni Corba (client e server) che utilizzano la libreria MyCorbaWorld, devono passare al metodo InitStuff il nome della macchina e il numero della porta sui quali il servizio di naming attivo e in ascolto; in generale le applicazioni ricevono queste informazioni come parametri di passaggio allinterno dei file di lancio. Se lapplicazione passa NShost = null oppure stringa vuota, allinterno del metodo InitStuff viene assegnato localhost; se non assegna NSdoor (che in tal caso vale 0), viene usato il valore default cio 12000. Nel solo caso di applicazione host, cio quando isServerApp vera, segue la risoluzione di RootPOA e listanziazione del Root POA Manager (membro privato org.omg.PortableServer.POAManager manager). Il metodo InitStuff restituisce come booleano lesito delle operazioni.

1.1.1.3. Connessione al servizio di naming. Il compito svolto dal metodo public boolean CatchNameService(); al netto del codice di gestione degli errori, le istruzioni operative sono solo due: obj = orb.resolve_initial_references("NameService"); nsRoot = NamingContextExtHelper.narrow(obj); 14/41

Riflessioni libere

ove obj di tipo org.omg.CORBA.Object, mentre nsRoot un membro della classe GestoreCorba, di tipo NamingContextExt.

1.1.1.4. Risoluzione del contesto applicativo. Gli oggetti server vengono istanziati nel namespace (contesto) applicativo VirgoSpace; la classe GestoreCorba espone il metodo seguente, per la risoluzione di un contesto: public boolean CatchNamespace(String ctxName)
Il metodo in grado di risolvere un qualsiasi contesto del quale noto il nome (parametro ctxName), e non il solo contesto VirgoSpace, tramite il seguente codice: NameComponent[] myCtx = new NameComponent[1]; myCtx[0] = new NameComponent(); myCtx[0].id = ctxName; myCtx[0].kind = ""; org.omg.CORBA.Object obj = nsRoot.resolve(myCtx); Fatto ci, necessario effettuare il narrowing di obj in un oggetto di tipo NamingContext; tuttavia qui il metodo perde generalit, perch il narrowing viene effettuato su un membro della classe GestoreCorba: vs = NamingContextHelper.narrow(obj); in pratica, vs (di tipo NamingContext) mantiene il riferimento al contesto VirgoSpace.

1.1.1.5. Creazione ed esposizione degli oggetti server. Gli oggetti server vengono istanziati ed esposti nel contesto VirgoSpace tramite il metodo seguente: public boolean InstanceServer(String srvName) { NameComponent[] myObj = new NameComponent[1]; myObj[0] = new NameComponent(); myObj[0].id = srvName; myObj[0].kind = "Singleton"; try { // switch su tutti i possibili oggetti server switch (srvName) { case "ServerPeople": vs.rebind(myObj, (new ServerPeople())._this(orb)); break; case "ServerSistema": vs.rebind(myObj, (new ServerSistema())._this(orb)); break; case "ServerHello": vs.rebind(myObj, (new ServerHello())._this(orb)); break;t case "ServerLibreriaFinto": vs.rebind(myObj, (new ServerLibreriaFinto())._this(orb)); break; default: <segnalazione derrore> return false; } } catch(...) { <segnalazione derrore> return false; } return true; } 15/41

Riflessioni libere
Listanziazione di un oggetto server qui effettuata in forma stringata; riferendomi a ServerPeople, il codice per esteso potrebbe essere il seguente: PeoplePOA peoPOA = new ServerPeople(); People peo = peoPOA._this(orb); vs.rebind(myObj, peo); la classe ServerPeople subtipo della classe astratta PeoplePOA, perci posso istanziare un oggetto di tipo ServerPeople riguardandolo come se fosse oggetto di tipo PeoplePOA, in modo da accedere i metodi della classe astratta, in particolare il metodo _this, il quale crea e ritorna un oggetto di tipo _PeopleStub, classe astratta che implementa linterfaccia Peopletutto quadra: loggetto stub viene registrato nel contesto, divenendo disponibile a ricevere le chiamate dei client, che poi inoltrer verso loggetto server cio peoPOA. Ecco un altro modo di scrivere il codice per esteso, equivalente a quello di cui sopra: ServerPeople peo = new ServerPeople(); vs.rebind(myObj, peo._this(orb)); Si noti luso di rebind e non di bind: rebind ha azione pi generale, se loggetto non mai stato registrato nel contesto si comporta come bind, se invece gi stato registrato lo sovrascrive, il che molto utile (possono rimanere registrazioni di oggetti vecchi, non pi presenti). Il valore del campo kind nella classe NameComponent non dirimente, il campo potrebbe anche essere lasciato vuoto; definire un oggetto server come Singleton non significa nulla di particolare. Gli oggetti Corba si comportano tutti come singleton, nel senso attribuito al termine nellarchitettura .NET. Tuttavia kind contribuisce a far parte del nome delloggetto, quindi se assegnato in fase di istanziazione, esso va utilizzato con lo stesso valore in fase di risoluzione. 1.1.1.6. Attivazione degli oggetti server. La classe GestoreCorba fornisce alle applicazioni host il metodo public boolean GoOrb(), allo scopo di iniziare il loop di attesa delle chiamate da parte dei client; il loop di durata indefinita. Il codice del metodo pu essere cos schematizzato, nella sua forma minimale: public boolean GoOrb() { try { manager.activate(); orb.run(); } catch (Exception e) { <segnalazione derrore> return false; } return true; } Entrambe le istruzioni sono necessarie; la prima non bloccante, ma la seconda lo : listruzione orb.run blocca il thread desecuzione dellhost. Lapplicazione host entra in esercizio, e i client possono richiamare i metodi degli oggetti server, tuttavia non bello che lapplicazione host resti bloccata, infatti diventa, tra le altre cose, impossibile terminare lapplicazione host quando desiderato dalloperatore. Si pu pensare di introdurre timer o altre forme di interruzione, tuttavia ho preferito implementare un meccanismo differente, basato sulluso dei thread: qui che interviene la classe CorbaThread. Limpiego della classe CorbaThread, che comporta una riscrittura del metodo GestoreCorba.GoOrb, verr studiato in un paragrafo apposito; per adesso, mi limito a dire che il funzionamento del metodo GoOrb resta concettualmente quello esposto, ossia la chiamata prima di manager.activate e poi di orb.run, ma in modo tale che lapplicazione host non si blocchi pi. 1.1.1.7. Risoluzione degli oggetti server (da parte dei client). I client possono accedere un oggetto server di cui conoscono il nome (definito nellIDL) mediante il seguente metodo, fornito dalla classe GestoreCorba:

16/41

Riflessioni libere
public Object CatchServer(String srvName) { org.omg.CORBA.Object obj = null; NameComponent[] myObj = new NameComponent[1]; myObj[0] = new NameComponent(); myObj[0].id = srvName; myObj[0].kind = "Singleton"; try { obj = vs.resolve(myObj); } catch(...) { <segnalazione derrore> return null; } try { // switch su tutti i possibili oggetti server switch (srvName) { case "ServerPeople": return PeopleHelper.narrow(obj); case "ServerSistema": return SistemaHelper.narrow(obj); case "ServerHello": return HelloHelper.narrow(obj); case "ServerLibreriaFinto": return LibreriaHelper.narrow(obj); } } catch(...) { <segnalazione derrore> return null; } // non passa mai di qui, serve solo per compilare return null; } ci che viene restituito lo stub, ad esempio nel caso di ServerPeople viene restituito _PeopleStub. Linvocazione dei metodi di un oggetto remoto avviene esattamente come se loggetto fosse locale; la esaminer in un successivo paragrafo.

1.1.1.8. Deallocazione degli oggetti server dal contesto del servizio di naming. Nel solo caso host, in fase di uscita dallapplicazione, ma prima di terminare loggetto ORB, necessario deallocare gli oggetti server dal contesto (VirgoSpace); a rigore, va tenuto presente che lapplicazione host pu deallocare uno o pi oggetti server senza terminare.
In ogni caso, la deallocazione di un oggetto server gestita dalla classe GestoreCorba tramite lapposito metodo: public boolean ShutServer(String srvName) { NameComponent[] myObj = new NameComponent[1]; myObj[0] = new NameComponent(); myObj[0].id = srvName; myObj[0].kind = "Singleton"; try { vs.unbind(myObj); }

17/41

Riflessioni libere
catch(...) { <segnalazione derrore> return false; } return true; } Alcuni controlli di esistenza sono stati tralasciati. Qui, srvName come sempre il nome delloggetto server definito nellIDL; il metodo vs.unbind elimina il riferimento (binding) delloggetto dal contesto di appartenenza identificato da vs. Ci che viene eliminato (deallocato) non tanto loggetto server, quanto il riferimento alloggetto nel contesto (VirgoSpace) del servizio di naming; torner in seguito su questo punto, e altri simili.

1.1.1.9. Terminazione delloggetto ORB. Quando unapplicazione Corba, sia essa host oppure client, termina, deve chiudere correttamente la connessione a Corba, cio terminare loggetto ORB da essa allocato alla partenza; la terminazione delloggetto ORB gestita dal metodo StopOrb della classe GestoreCorba: public boolean StopOrb(boolean isServerApp) { try { if (isServerApp) orb.shutdown(false); orb.destroy(); } catch(Exception ex) { <segnalazione derrore> return false; } return true; }
Nel solo caso di unapplicazione host, va invocato il metodo shutdown sullistanza di ORB: questo si preoccupa di far terminare i vari oggetti POA (gli adapter degli oggetti server); il parametro di passaggio al metodo shutdown serve a imporre o meno alloggetto ORB lattesa del completamento delle richieste correnti: passando false, si impone alloggetto ORB di procedere allo shutdown senza attendere. Linvocazione del metodo destroy causa leffettiva distruzione dellistanza.

Descrizione della classe CorbaThread.


Nella classe GestoreCorba, definisco il membro private CorbaThread torba = null. Il membro torba usato dalla sola routine GoOrb, che ha il codice seguente: public boolean GoOrb() { try { manager.activate(); } catch (AdapterInactive e) { <segnalazione derrore> return false; } torba = new CorbaThread(orb); torba.start(); return true; } Come gi detto, listruzione manager.activate non bloccante, a differenza di orb.run: solo questultima viene quindi eseguita in un thread separato, distinto dal thread principale di esecuzione dellhost. Il nuovo thread viene creato istanziando la classe CorbaThread, della quale fornisco il codice:

18/41

Riflessioni libere
public class CorbaThread extends Thread { ORB orb = null; public CorbaThread(ORB orb) { this.orb = orb; } @Override public void run() { orb.run(); } } Quando loggetto viene creato, riceve il riferimento alloggetto orb; quando viene invocato torba.start, allora viene eseguito il metodo run: qui che viene eseguita listruzione bloccante orb.run, e infatti il thread in cui essa viene eseguita si blocca, ma si tratta di un thread ad hoc, distinto dal thread principale di esecuzione dellapplicazione host. La routine run termina, e con essa il thread separato, quando viene eseguita listruzione orb.shutdown nel codice del metodo GestoreCorba.StopOrb.

Applicazione host prototipale: GenericCorbaHost.


Lapplicazione (Corba) GenericCorbaHost un host capace di istanziare e attivare uno o pi oggetti server, contenuti nella libreria MyCorbaWorld, inclusa dallapplicazione. GenericCorbaHost stata sviluppata mediante un Java project, ha natura di applicazione desktop grafica, e utilizza Swing. GenericCorbaHost ha le seguenti funzionalit: a) primaria implementa il ciclo di funzionamento di un host nominale b) secondaria agevola lo studio del ciclo di vita degli oggetti Corba Lapplicazione consiste di un solo file sorgente, MainDlg.java; di seguito, viene esposta la struttura software dellapplicazione con particolare riferimento allimplementazione della funzionalit primaria, poi viene illustrato il build dellapplicazione, infine le caratteristiche di lancio. La funzionalit secondaria viene studiata nel seguito, in quanto per comprenderne il significato utile illustrare prima il funzionamento di una applicazione client prototipale.

Inizializzazione.
La classe MainDlg stata creata entro il package app, mediante lapposito wizard: NewOtherWindow BuilderSwing DesignerApplication Window. La classe MainDlg ha alcuni membri privati di interesse: i) private static String NShost = null e private static int NSdoor = 0, rispettivamente nome del computer e porta sui quali trovare il servizio di naming, che si suppone siano passati come parametri di lancio ii) GestoreCorba gestCorba (inizialmente null) iii) final String[] allSrv = {...}: contiene lelenco di tutti gli oggetti server accessibili allhost, in pratica tutti quelli presenti nel package srvCorba della libreria MyCorbaWorld; al momento, {"ServerPeople","ServerSistema","ServerHello","ServerLibreriaFinto"} ogni nuovo oggetto server dovrebbe essere inserito in questa lista iv) String[] srvActiveList = new String[allSrv.length]: contiene lelenco degli oggetti server selezionati, pronti per lattivazione; allinizio la lista vuota (ogni stringa ha lunghezza zero). Il main assegna, se presenti, args[0] e args[1] rispettivamente a NShost e NSdoor, quindi istanzia la classe MainDlg e poi rende visibile il frame; il wizard cra un costruttore senza parametri, che consiste della sola chiamata alla routine initialize, e questa a sua volta si limita a creare il frame; tutto il codice restante della classe stato scritto da me. Aggiungo al build path la libreria, C:\LibJar\MyCorbaWorld.jar, della quale utilizzo i tipi.

19/41

Riflessioni libere
Quanto segue la form costruita con Window Builder Editor:

Sorvoler sulle parti grafiche, per quanto possibile senza perdere chiarezza. Il costruttore inizia chiamando la routine initialize, che costruisce la form sopra rappresentata; vengono definiti anche gli event handler, che verranno spiegati nel seguito. Poi il costruttore inizializza Corba: gestCorba = new GestoreCorba(); if (!gestCorba.InitStuff(true,NShost,NSdoor)) { outLab.setText("Errore in GestoreCorba.InitStuff"); return; } if (!gestCorba.CatchNameService()) { outLab.setText("Errore in GestoreCorba.CatchNameService"); return; } if (!gestCorba.CatchNamespace("VirgoSpace")) { outLab.setText("Errore in GestoreCorba.CatchNamespace"); return; } outLab la label sul fondo della form, se non ci sono errori resta la scritta iniziale All is good. Il codice precedente implementa le prime tre fasi del ciclo di funzionamento nominale di un host. In ultimo, il costruttore popola la lista degli oggetti server con la lista di nomi presenti in allSrv. Con ci, linizializzazione conclusa; a runtime, ecco come si presenta lapplicazione:

20/41

Riflessioni libere

Funzionamento a regime.
Loperativit primaria fornita dai primi due tasti in alto, dalla lista a fianco (che ammette la selezione multipla), e dalla label in basso (con sole funzioni di visualizzazione di eventuali errori). Loperatore pu selezionare uno o pi oggetti server della lista; se vuole annullare tutte le selezioni, ha a disposizione il tasto Deseleziona. Una volta decisi gli oggetti che intende istanziare, loperatore preme il tasto Istanzia: gli oggetti scelti vengono istanziati, mentre i due tasti e la lista vengono disabilitati, per quanto concerne la funzionalit primaria lapplicazione pu a questo punto solo terminare. Levent handler del tasto Istanzia, per ogni stringa di lunghezza > 0 dellarray srvActiveList, effettua la chiamata gestCorba.InstanceServer(srvActiveList[p]); poi attiva tutti gli oggetti istanziati tramite la chiamata gestCorba.GoOrb(). Ci implementa le fasi quarta, quinta e sesta del ciclo di funzionamento nominale di un host.

Terminazione.
Loperatore pu in ogni momento terminare lapplicazione tramite il system menu; alluscita, viene eseguito levent handler seguente (definito nella routine initialize): public void windowClosing(WindowEvent e) { // shut all servers and then shut Corba int i = 0; for (i=0;i<srvActiveList.length;i++) if (srvActiveList[i].length() > 0) gestCorba.ShutServer(srvActiveList[i]); gestCorba.StopOrb(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } Tutti gli oggetti server attivati vengono deallocati e loggetto ORB terminato; il codice precedente implementa quindi le ultime due fasi del ciclo di funzionamento nominale di un host.

Build.
La libreria MyCorbaWorld non include alcun JAR, in modo da rimanere di piccole dimensioni; spetta alle applicazioni che la utilizzano includere i JAR necessari. Nel caso di GenericCorbaHost, il numero dei JAR inclusi cresce al crescere degli oggetti server istanziabili, anche se non necessario includerli tutti. Ad esempio, uno degli oggetti trattati da GenericCorbaHost ServerPeople: questo oggetto utilizza Oracle; se si vuole istanziare loggetto ServerPeople servendosi di GenericCorbaHost, allora il build di GenericCorbaHost

21/41

Riflessioni libere
deve includere ojdbc6.jar (o equivalente). Se non includessi questo JAR, potrei ugualmente generare lapplicazione host e utilizzarla, a patto di non pretendere poi di istanziare e utilizzare loggetto ServerPeople. Il build di GenericCorbaHost non include le librerie Corba, in quanto queste vengono accedute a runtime, come si vedr nel paragrafo successivo; in generale, posso operare questo tipo di scelta ogni volta che le librerie Corba risultino accessibili allapplicazione (host o client, non importa).

Esecuzione.
Lapplicazione GenericCorbaHost viene lanciata tramite il comando launchGenericCorbaHost.bat:
java -Xbootclasspath/p:"C:\Orbacus\lib\OB.jar;C:\Orbacus\lib\OBNaming.jar" -jar GenericCorbaHost.jar %1 %2

per ogni considerazione in merito, si pu vedere il paragrafo relativo alla sovrascrittura di rt.jar. I parametri di passaggio %1 e %2 permettono il passaggio di NShost e NSdoor; se non vengono passati, il programma cerca il servizio di naming su localhost, porta 12000.

Importante variazione del funzionamento nominale di un host.


Nellapplicazione GenericCorbaHost, linvocazione del metodo GestoreCorba.GoOrb viene fatta dopo aver istanziato gli oggetti server; tuttavia linvocazione di GestoreCorba.GoOrb pu essere anticipata: infatti ci che viene attivato loggetto ORB, e anche il POAManager, e non i singoli oggetti server. Sulla base di queste considerazioni, pongo la chiamata al metodo GestoreCorba.GoOrb nella fase di inizializzazione, dopo la risoluzione del contesto VirgoSpace (chiamata GestoreCorba.CatchNamespace), togliendola quindi dallevent handler del tasto Istanzia: tutto funziona ancora correttamente. Il funzionamento nominale di unapplicazione Corba host il seguente: 1) istanziare gli oggetti ORB e POAManager 2) connettersi al servizio di naming 3) risolvere il contesto in cui risiedono gli oggetti server 4) attivare gli oggetti ORB e POAManager 5) istanziare gli oggetti server 6) esporre gli oggetti istanziati nel contesto del servizio di naming prima risolto 7) deallocare gli oggetti server dal contesto, quando lapplicazione host cessa 8) terminare correttamente loggetto ORB, quando lapplicazione host cessa. Di qui in poi, adotter questo come modello di funzionamento host, e manterr lultima versione di GenericCorbaHost, conforme al nuovo modello; le funzionalit primaria e secondaria restano invariate.

Applicazione client prototipale: HelloClient.


Lapplicazione (Corba) HelloClient puramente accademica; ha due funzioni: mostrare il ciclo di funzionamento nominale di un client, e agevolare lo studio del ciclo di vita degli oggetti Corba. HelloClient unapplicazione desktop, con grafica Swing; consiste di un solo file sorgente, HelloWnd.java.

Note sulloggetto ServerHello.


Loggetto ServerHello fa parte della libreria MyCorbaWorld; specificato da Hello.idl: module greetings { interface Hello { string say_hello(in string msg); }; }; Ecco il codice di ServerHello: public class ServerHello extends HelloPOA { @Override public String say_hello(String msg) {

22/41

Riflessioni libere
return "Hello client, my name is ServerHello; here's your text back: "+msg;

La sua istanziazione avviene ad opera di GenericCorbaHost; ipotizzo nel seguito che HelloClient trovi loggetto server gi disponibile.

Inizializzazione.
La classe HelloWnd stata creata entro il package app, mediante lapposito wizard: NewOtherWindow BuilderSwing DesignerApplication Window. La classe HelloWnd ha i membri NShost e NSdoor, definiti e utilizzati come in GenericCorbaHost per risolvere il servizio di naming. Il main assegna args[0] e args[1], se presenti, ai membri NShost e NSdoor, quindi istanzia la classe HelloWnd e poi rende visibile il frame; il wizard crea un costruttore senza parametri, che consiste della sola chiamata alla routine initialize, e questa a sua volta si limita a creare il frame; tutto il codice restante della classe stato scritto da me. Aggiungo al build path la libreria, C:\LibJar\MyCorbaWorld.jar, della quale utilizzo i tipi. Quanto segue la form costruita con Window Builder Editor:

La classe HelloWnd ha il membro privato GestoreCorba gestCorba (inizialmente null). Il costruttore inizia assegnando lora corrente al membro private String idClient, nel formato hh:mm:ss, la stringa diverr lidentificativo atto a discriminare le istanze simultanee di HelloClient. Poi il costruttore prosegue chiamando la routine initialize, che costruisce la form sopra rappresentata; vengono definiti anche gli event handler dei tasti Catch e Go, che rispettivamente richiama il metodo delloggetto ServerHello. La stringa idClient viene posta nel titolo della window, ben visibile. Infine il costruttore inizializza Corba: gestCorba = new GestoreCorba(); if (!gestCorba.InitStuff(false,NShost,NSdoor)) { textArea.setText("Errore in GestoreCorba.InitStuff"); return; } if (!gestCorba.CatchNameService()) { textArea.setText("Errore in GestoreCorba.CatchNameService"); return; } if (!gestCorba.CatchNamespace("VirgoSpace")) { textArea.setText("Errore in GestoreCorba.CatchNamespace"); return; }

23/41

Riflessioni libere
textArea la JTextArea in basso nella form, che ospita la risposta delloggetto ServerHello oppure leventuale scritta di errore. Il tastino sopra la text area, a sinistra, serve a cancellare la text area. Il codice precedente implementa le prime tre fasi del ciclo di funzionamento nominale di un client; si noti che loggetto server, una volta risolto, viene acceduto tramite linterfaccia Hello, derivante dallIDL.

Funzionamento a regime.
Finita linizializzazione, il tasto Catch appare in rosso: loggetto server devessere ancora risolto. Quindi, loperatore dovr premere il tasto Catch, del quale segue il codice dellevent handler: hello = (Hello)gestCorba.CatchServer("ServerHello"); if (hello == null) { textArea.setText("Errore in GestoreCorba.CatchServer(Hello)"); btnCatch.setBackground(Color.RED); } else btnCatch.setBackground(Color.GREEN); se loggetto server viene correttamente risolto, allora il tasto assume il colore verde. Questa la quarta fase del ciclo di funzionamento nominale di un client. Loperatore inserisce un testo (qualsiasi) nella textbox, poi preme il tasto Go. Levent handler del tasto Go implementa la quinta fase del ciclo di funzionamento client, infatti chiama il metodo delloggetto server, passando il contenuto della textbox seguito dallidentificativo idClient: try { textArea.setText(hello.say_hello(textField.getText()+" ["+idClient+"]")); } catch (Exception exc) { textArea.setText("Calling error on ServerHello: " + exc.getMessage()); btnCatch.setBackground(Color.RED); } se la chiamata al metodo fallisce, il tasto Catch viene posto rosso, indicando cos alloperatore la necessit di tentare nuovamente la risoluzione delloggetto server. Ecco come appare lapplicazione funzionante, a runtime:

Si noti dal codice precedente che il metodo delloggetto server remoto viene chiamato esattamente come se si trattasse del metodo di un oggetto locale.

Terminazione.
Loperatore pu in ogni momento terminare lapplicazione tramite il system menu; alluscita, viene eseguito levent handler seguente (definito nella routine initialize): public void windowClosing(WindowEvent e) {

24/41

Riflessioni libere
// shut Corba gestCorba.StopOrb(false); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Loggetto ORB viene terminato; il codice precedente implementa quindi la sesta e ultima fase del ciclo di funzionamento nominale di un client.

Build ed esecuzione.
Il build di HelloClient include la sola libreria MyCorbaWorld; siccome client solo di ServerHello, non ha bisogno di includere altri JAR. Lapplicazione HelloClient viene lanciata tramite il comando launchHelloClient.bat: java -Xbootclasspath/p:"C:\Orbacus\lib\OB.jar;C:\Orbacus\lib\OBNaming.jar" -jar HelloClient.jar %1 %2 per ogni considerazione in merito, si pu vedere il paragrafo relativo alla sovrascrittura di rt.jar. I parametri di passaggio %1 e %2 permettono il passaggio di NShost e NSdoor; se non vengono passati, il programma cerca il servizio di naming su localhost, porta 12000.

Considerazioni sul ciclo di vita degli oggetti Corba.


Le considerazioni seguenti si basano sullutilizzo della funzionalit secondaria di GenericCorbaHost in associazione allapplicazione HelloClient.

Modifiche ad hoc alla libreria MyCorbaWorld.


Ho apportato le seguenti modifiche alla libreria MyCorbaWorld: 1. ho aggiunto alla classe ServerHello un identificativo univoco pubblico, proprio di ogni singola istanza; inoltre ho modificato il metodo say_hello (cambiando anche Hello.idl) 2. ho aggiunto alla classe _HelloStub un identificativo univoco pubblico, proprio di ogni singola istanza 3. nella classe GestoreCorba, ho aggiunto il membro private Hello stubHello 4. ancora nella classe GestoreCorba, ho aggiunto i metodi CreateServerHello, BindServerHello e NullifyServerHello, richiamati da GenericCorbaHost premendo i tasti, rispettivamente, Crea Hello, Bind Hello e Nullify Hello. Chiarir nel seguito perch abbia scelto di intervenire proprio su _HelloStub.

1.1.1.10. Nuova classe ServerHello. Loggetto ServerHello viene modificato allo scopo di rendere immediata la distinzione tra istanze diverse; ecco il nuovo codice della classe (tralascio la gestione degli errori): public class ServerHello extends HelloPOA { File f = null; public ServerHello() { f = File.createTempFile("ServerHello", ".ide", null); f.deleteOnExit(); } public String idServerHello() { return f.getName(); } @Override public String say_hello(String client,String msg) { // TODO Auto-generated method stub return "Hello client " + client + ", I'm " + idServerHello() + "; here's your text back: " + msg; } } 25/41

Riflessioni libere
Con ci, ogni istanza di ServerHello ha un proprio identificativo univoco, ossia il nome di un file temporaneo; il file viene cancellato automaticamente alla distruzione delloggetto5. I client passano al metodo say_hello il loro identificativo, ad esempio le istanze HelloClient passano il valore del campo idClient hh:mm:ss. La modifica di Hello.idl obbliga allaggiornamento di tutti i client, anche quelli che non posseggono un identificativo (i quali passeranno una stringa nuova), quindi una misura onerosa; in Java avrei potuto utilizzare meccanismi di overload oppure varargs (metodi con parametri opzionali), ma il linguaggio IDL di Corba non permette overload o parametri opzionali.

1.1.1.11. Nuova classe _HelloStub. Unanaloga modifica viene compiuta sulla classe _HelloStub, appartenente al package greetings: si tratta di una classe generata automaticamente dallutility Jidl, tuttavia finch non richiamo il target <idlGen> di build.xml, la classe _HelloStub persiste e persistono pure le modifiche da me apportate. Aggiungo al codice preesistente quanto segue: File f = null; public _HelloStub() { f = File.createTempFile("StubHello", ".ide", null); f.deleteOnExit(); } public String idHelloStub() { return f.getName(); }
Cos ogni istanza di _HelloStub ha un proprio identificativo univoco, ossia il nome di un file temporaneo; il file viene cancellato automaticamente alla distruzione delloggetto.

1.1.1.12. Metodo CreaServerHello. Riscrivo il codice di creazione delloggetto ServerHello, senza farne il binding, nella forma esplosa gi illustrata trattando la creazione di oggetti server: private Hello stubHello = null; public String CreateServerHello() { ServerHello srvHello = new ServerHello(); String idSrv = srvHello.idServerHello(); stubHello = srvHello._this(orb); srvHello = null; return "<html>"+idSrv+"<BR>"+ ((_HelloStub)stubHello).idHelloStub()+"</html>"; }
Si noti che nullifico loggetto srvHello, come si vedr questo non inficia il funzionamento; potrei sotto certe condizioni nullificare anche stubHello senza inficiare il funzionamento, come vedr pi avanti, qui non lo faccio perch voglio mostrare la ripetibilit delloperazione di bind. Il formato particolare della stringa di ritorno adatto alla rappresentazione grafica entro la label lblStubId di GenericCorbaHost.

1.1.1.13. Metodo BindServerHello. Qui riscrivo il metodo GestoreCorba.InstanceServer, specificamente per la classe ServerHello e senza la creazione dellistanza, gi effettuata dal metodo CreateServerHello: public boolean BindServerHello() { NameComponent[] myObj = new NameComponent[1]; myObj[0] = new NameComponent(); myObj[0].id = "ServerHello"; myObj[0].kind = "Singleton";
5

Siccome il terzo parametro null, i file temporanei vengono creati (vuoti) entro una directory predefinita; quale sia precisamente questa directory, dipende dal sistema operativo e da setting particolari (del sistema o dellutente). Se avessi voluto porli in una directory specifica, ad esempio C:\Temp, avrei dovuto scrivere File.createTempFile("ServerHello", ".ide", new File("C:\\Temp")).

26/41

Riflessioni libere
try {

vs.rebind(myObj, stubHello); } catch(...) { <segnalazione derrore> return false; } return true; } Come si vede, il metodo presuppone che il membro stubHello sia gi stato creato nella forma adeguata, questo il motivo per cui lho reso permanente, lo scopo separare la creazione delloggetto dal suo binding nel contesto VirgoSpace.

1.1.1.14. Metodo NullifyServerHello. Il metodo si limita a nullificare stubHello: public void NullifyServerHello() { stubHello = null; }
Si tratta di un metodo del tutto ad hoc, che verr usato per ripetere non solo il binding ma anche la creazione delloggetto; ha valore puramente strumentale.

Modifiche a HelloClient.
sufficiente aggiornare la chiamata del metodo say_hello, allinterno dellevent handler del tasto Go: textArea.setText(hello.say_hello(idClient,textField.getText())); lapplicazione client passa al metodo say_hello il proprio identificativo, cio hh:mm:ss.

Modifiche a GenericCorbaHost.
Lapplicazione host scrive la stringa di ritorno dalla chiamata del metodo GestoreCorba.CreateServerHello (effettuata dallevent handler del tasto Crea Hello) nella label lblStubId, gi presente ma non usata in precedenza (in fase di design la sua presenza segnalata tramite alcuni asterischi).

Scenario generale di funzionamento.


Lo scenario generale di funzionamento semplice: GenericCorbaHost completa la sua inizializzazione (quindi crea loggetto ORB, risolve il contesto applicativo VirgoSpace e attiva gli oggetti ORB e POAManager); a parte, viene lanciato il client, HelloClient. Poi loperatore preme, nellordine, i tasti Crea Hello e Bind Hello: cos facendo, loggetto ServerHello viene creato e registrato nel contesto VirgoSpace; a questo punto, lapplicazione HelloClient pu interagire con loggetto server, risolvendolo e poi chiamandone pi volte il metodo say_hello. In ogni momento, lapplicazione GenericCorbaHost permette alloperatore di eliminare il riferimento alloggetto server dal contesto di naming (tasto Unbind Hello), e anche di annullare loggetto server (tasto Nullify Hello); inoltre loperatore pu ricollegare loggetto server al contesto VirgoSpace, o anche istanziare nuovamente loggetto, servendosi rispettivamente dei tasti Bind Hello e Crea Hello. Sono quindi possibili molti scenari di dettaglio; nel seguito esaminer alcuni di essi. Ciascun scenario di dettaglio una sequenza di operazioni; in generale le varie sequenze significative presenteranno pezzi comuni, ad esempio tutte inizieranno con lhost, creando ed esponendo loggetto server, e solo dopo ci verr attivata sul client la risoluzione delloggetto se facessi diversamente, riceverei un errore. Similmente, le sequenze significative termineranno con la chiusura del client, eventualmente seguita dalla chiusura definitiva dellhost ( possibile che chiuda e riapra lhost, con client ancora in funzione); sono possibili sequenze (scenari) in cui giocano pi client contemporanemente, in tal caso mi aspetto che la sequenza termini con la chiusura dellultimo client, eventualmente seguita dalla chiusura definitiva dellhost. Attivo per primo lhost; ignoro la lista a destra e premo il tasto Crea Hello:

27/41

Riflessioni libere

Riprendo in esame il codice del metodo CreateServerHello: loggetto di tipo ServerHello viene istanziato, viene recuperato il suo identificativo, poi da esso viene creato loggetto stub, cio stubHello, e a questo punto loggetto di tipo ServerHello viene nullificato, ormai non serve pi. Gli identificativi sia delloggetto ServerHello, sia dello stub, vengono restituiti e quindi visualizzati in lblStubId. Poi premo il tasto Bind Hello: con ci, loggetto ServerHello viene registrato nel contesto VirgoSpace. A questo punto, supponiamo che io prema Nullify Hello, quindi nullifichi anche stubHello: ormai loggetto ServerHello registrato nel servizio di naming e quindi a disposizione dei client. Attivo due istanze client, che compiono (in momenti diversi) le stesse operazioni, cio risolvono loggetto server e chiamano say_hello con lo stesso valore nella textbox: come desiderato, posso ancora distinguere i due client in base al loro identificativo.

Osservando la directory dei file temporanei, scopro che c un unico file ServerHello<numero>.ide, ma ci sono tre file StubHello<numero>.ide diversi: uno dei tre quello che compare in lblStubId di GenericCorbaHost, gli altri sono stati generati in seguito. Suppongo che i client effettuino altre chiamate al metodo say_hello: non vengono creati nuovi file StubHello<numero>.ide. Se per uno qualsiasi dei client preme il tasto Catch, quindi invoca il metodo GestoreCorba.CatchServer("ServerHello"), un nuovo file StubHello<numero>.ide viene creato, mentre nessun file ServerHello<numero>.ide viene creato. Il meccanismo chiaro: ogni volta che un client risolve loggetto server, viene creato un file temporaneo StubHello<numero>.ide. Ci significa che ogni volta che un client risolve loggetto server, il metodo rebind crea un nuovo oggetto di tipo _HelloStub e lo restituisce al client.

28/41

Riflessioni libere

C un punto critico da spiegare: io ho nullificato loggetto server (nel codice del metodo GestoreCorba.CreaServerHello), gi ancor prima di ogni connessione client; e allora, chi risponde ? Non pu essere un oggetto server istanziato on the fly, altrimenti vedrei apparire un nuovo file ServerHello<numero>.ide, come accade nel caso degli oggetti stub. La spiegazione la seguente: una volta che un oggetto server stato registrato nel servizio di naming, loggetto permane ed disponibile a ogni client finch non viene effettuata la sua unbind. Listanziazione esplicita delloggetto server, e anche del suo stub, servono per poter registrare loggetto server nel servizio di naming, ma poi non servono pi, listanza registrata nel servizio di naming vive di vita propria e va avanti finch non avviene loperazione di unbind. Adesso suppongo di premere il tasto Unbind Hello sullhost. I client che hanno gi risolto loggetto server possono continuare a invocarne i metodi: quindi lunbind non ha veramente distrutto listanza delloggetto server che era registrata in VirgoSpace, si limitata a cancellarne il riferimento dal contesto infatti la Orbacus console non la mostra pi, per le richieste dei client continuano ad essere servite, e listanza che le serve proprio quella con lidentificativo originale ServerHello6454494375715528649.ide. Se un nuovo client tenta di risolvere loggetto, riceve un errore Corba, mentre i due gi connessi continuano a funzionare. Suppongo per che il client 11:24:19 (vedi sopra) prema il tasto Catch: anche qui, si verifica un errore Corba; laltro client, il 11:23:22, continua invece a funzionare:

Quando il client 11:24:19 esce, i file StubHello<numero>.ide creati in seguito alle sue operazioni di resolve vengono automaticamente cancellati, ci significa che gli oggetti stub corrispondenti vengono riciclati. Si pu concludere che loggetto server dopo lunbind non viene distrutto, viene solo eliminato il suo riferimento dal contesto di naming, pu quindi continuare a servire le richieste dei client che lhanno gi risolto ma non pi ulteriormente risolvibile. Quando anche il client 11:23:22 termina, vengono eliminati tutti i restanti file StubHello<numero>.ide, tranne quello (poco significativo) generato non da una rebind ma alla creazione delloggetto server. Siccome ho nullificato stubHello, non posso effettuare nuovamente la rebind premendo Bind Hello, posso solo o ricreare sia oggetto server sia stub premendo il tasto Crea Hello, o terminare lhost nel qual caso, il file ServerHello<numero>.ide e lultimo dei file StubHello<numero>.ide vengono cancellati. Considero adesso uno scenario diverso: inizio con lhost premendo Crea Hello e Bind Hello, ma non premo Nullify Hello, poi procedo come nel caso precedente, con i client che risolvono loggetto server e ne invocano i metodi. Come nel caso precedente, dopo un po premo Unbind Hello, che deregistra loggetto server dal contesto: loggetto server resta funzionante per i client che lhanno gi risolto, ma non pi risolubile; posso pormi nel caso sopra raffigurato, con un client funzionante e uno in errore. A questo punto, premo il tasto Bind Hello: stavolta stubHello non stato nullificato, per cui la rebind va a buon fine. Anche il client che era andato in errore pu risolvere nuovamente il server. Inoltre loggetto server continua ad essere sempre lo stesso: loggetto che era stato deregistrato viene nuovamente registrato, infatti non c nessun nuovo file ServerHello<numero>.ide.

29/41

Riflessioni libere

Sintesi conclusiva.
Lo scenario generale di funzionamento semplice: GenericCorbaHost completa la sua inizializzazione

Costruzione della Axis2 web service LibreriaWS.


La web service LibreriaWS stata costruita usando Eclipse Indigo, seguendo la metodologia bottomup.

Impostazione del progetto Eclipse LibreriaWS.


stato utilizzato un Java project, di nome LibreriaWS; creo il package ws, e allinterno di ws creo la classe LibreriaWS, destinata ad implementare la web service: si tratta di una classe senza particolarit, non subipo di altre classi e non implementa alcuna interfaccia. Oltre ad essa, creo in ws le classi dei dati: Autore, Libro, LibroDn e MixLibro. Poi creo il folder META-INF, allo stesso livello di src; entro META-INF creo il file services.xml. Questo file stabilisce quale sia la classe web service, e inoltre definisce la lista dei web method (detti operation): <service name="LibreriaWS"> <description>Libreria data provider</description> <parameter name="ServiceClass">ws.LibreriaWS</parameter> <operation name="DataRefresh"> <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> </operation> ... <operation name="SystemInfo"> <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> </operation> </service> La classe LibreriaWS contiene tutti i web method, definiti come pubblici. In generale, questi metodi potrebbero anche essere vuoti, e non servirebbero nemmeno le classi dati.

Fase di build.
Il file build.xml ha struttura consueta, ossia composto dai target clean, init, compile, jarGen, copy. Come di regola, utilizzo la directory build per costruire il JAR, tuttavia c una peculiarit legata alle Axis2 web service: build contiene la solita directory classes, ma questultima, oltre ai package omonimi della directory src qui ws, contiene anche la directory META-INF. La directory META-INF deve contenere services.xml, in modo che questultimo possa essere incluso nella generazione del JAR. Con ci, il target <compile> contiene, oltre al comando javac, che popola build.classes con le classi del package ws, anche il comando di copia del file services.xml da <dir_progetto>/META-INF a <dir_progetto>/build/classes/META-INF. Poi il target <jarGen> si preoccupa della generazione del file LibreriaWS.jar, contenente anche il driver JDBC per Oracle. Il target finale, <copy>, scrive il file nella directory repository, qui C:\aar files, cambiandone lestensione: ottengo cos C:\aar files\LibreriaWS.aar.

Installazione.
Larchivio C:\aar files\LibreriaWS.aar la Axis2 web service, ma per funzionare deve essere inserita nel giusto contesto. Con Tomcat attivo, lancio lutility Axis2 Manager: passando http://localhost:8081/axis2 nel web browser, accedo alla console Axis2. Utilizzo il link Administration, con Username = admin e password = axis2; nel menu Tools a sinistra, scelgo Upload Service: tramite la form, accedo larchivio C:\aar files\LibreriaWS.aar e ne faccio lupload, ed finita. Dopo loperazione di upload, trovo la web service copiata entro la struttura di Tomcat: il file LibreriaWS.aar presente in webapps/axis2/WEB-INF/services (webapps la root di Tomcat). Se dovessi in seguito aggiornare la web service, dovrei prima di tutto cancellare il file LibreriaWS.aar.aar dalla directory di Tomcat, poi seguire la procedura di installazione gi vista.

30/41

Riflessioni libere

WSDL, linterfaccia verso i client.


La descrizione della web service, nel linguaggio WSDL, messa a disposizione per i client, la seguente: http://localhost:8081/axis2/services/LibreriaWS?wsdl, accessibile ad esempio tramite browser.

Realizzazione di un client di LibreriaWS.


Il progetto di LibreriaWS non si riferisce mai a axis2: non vengono usate le librerie presenti in axis2/lib, n i tool presenti in axis2/bin. Laggancio allambiente axis2 automatico, in buona parte posizionale e in parte dichiarativo (il file, di estensione particolare AAR, deve stare in una certa directory di Tomcat, sul quale stato installato il sistema axis2, inoltre devessere presente services.xml, etc). Il discorso completamente diverso per i client: questi sono applicazioni di natura qualsiasi, non hanno alcun legame automatico al mondo axis2, quindi la costruzione del legame tra il client e la web service per cos dire a carico del client, ed realizzato tramite le librerie e i tool di axis2.

Impostazione del progetto client: LibreriaWSClient.


Il client LibreriaWSClient unapplicazione console, molto semplice, ma il meccanismo di riferimento allambiente axis2 del tutto generale, valido per ogni client. Lapplicazione costruita con un Java project e il solo sorgente applicativo Client.java, nel package app. Per la sua descrizione conviene attenersi alle diverse fasi del build file, build.xml; le prime due sono di routine: il tag <init> genera la solita directory build, con la sottodirectory classes, il tag <clean> la cancella.

1.1.1.15. Generazione delle classi stub per il colloquio con la web service. Il terzo tag del build file <WSDL2JAVA>: <target name="WSDL2JAVA" description="Stub generation"> <java classname="org.apache.axis2.wsdl.WSDL2Java" fork="true"> <classpath refid="axis2.classpath"/> <arg value="-uri"/> <arg value="${wsdlSource}"/> <arg value="-p"/> <arg value="${stubPkg}"/> <arg value="-d"/> <arg value="adb"/> <arg value="-s"/> </java> </target>
Viene messa in esecuzione la classe WSDL2Java.class contenuta nel package org.apache.axis2.wsdl della libreria axis2-codegen-1.6.1.jar; il classpath linsieme dei file JAR contenuti in axis2-1.6.1/lib. In sostanza, equivale al seguente comando, passato al command prompt (non importa la directory di lancio): java cp %AXIS2_HOME%/lib/axis2-codegen-1.6.1.jar;%AXIS2_HOME%/lib/axis2-kernel-1.6.1.jar; %AXIS2_HOME%/lib/commons-logging-1.1.1.jar org.apache.axis2.wsdl.WSDL2Java in realt, come si vedr a breve, la lista delle librerie nel classpath insufficiente, con essa posso solo lanciare la classe, senza passarle alcun parametro. La lista dei subtarget <arg> esprime linsieme di parametri da passare alla classe WSDL2Java; equivale al comando seguente, passato al command prompt: java -cp "%AXIS2_HOME%/lib/axis2-codegen-1.6.1.jar;%AXIS2_HOME%/lib/axis2-kernel-1.6.1.jar; %AXIS2_HOME%/lib/commons-logging-1.1.1.jar;%AXIS2_HOME%/lib/wsdl4j-1.6.2.jar; %AXIS2_HOME %/lib/neethi-3.0.1.jar;%AXIS2_HOME%/lib/axiom-api-1.2.12.jar; %AXIS2_HOME%/lib/XmlSchema-1.4.7.jar; %AXIS2_HOME%/lib/axiom-impl-1.2.12.jar; %AXIS2_HOME%/lib/axis2-adb-codegen-1.6.1.jar; %AXIS2_HOME%/lib/axis2-adb-1.6.1.jar" org.apache.axis2.wsdl.WSDL2Java -uri "http://localhost:8081/axis2/services/LibreriaWS?wsdl" -p stubLibWS -d adb -s Il file WSDL descrittivo della web service specificato dallopzione uri; il nome del package a cui appartengono i file generati dalla classe WSDL2Java specificato tramite lopzione p; il tipo di databinding 6
6

Nel mondo delle web service, il termine databinding si riferisce alle tecniche di conversione dei dati, passando dai dati XML alle strutture dati applicative (interne alle applicazioni client e server), e viceversa. Il framework axis2 ha un databinding proprio, che adb (axis2 data binding), ma pu operare anche con altri tipi di databinding, come XMLBeans e JiBX; lopzione d dice alla classe WSDL2Java quale modello di databinding

31/41

Riflessioni libere
scelto adb (axis2 databinding), come specificato dallopzione d; lopzione s stabilisce la modalit sincrona di colloquio (il client dopo aver effettuato uan chiamata, si blocca in attesa della risposta). Il comando precedente equivalente allutilizzo del target <WSDL2JAVA> del build file. Come risultato, viene generato il file LibreriaWSStub.java entro il package stubLibWS (situato entro la directory src). Supponendo di aver gi compilato e generato il file JAR, il progetto si presenta come a sinistra. Il target <compile> non presenta peculiarit: compila le classi dei package app e stubLibWS e le pone entro la directory build. Il target <jarGen> come si vedr prevede una lista di librerie axis2, necessarie. Il file manifest.txt definisce la main class, che app.Client. Il risultato della fase <jarGen> build/LibreriaWSClient.jar. Le librerie presenti nel nodo Referenced Libraries sono state aggiunte tramite Configure Build Path, add external JARs cercandole nella directory AXIS2_HOME/lib. Queste librerie sono un sottoinsieme ridotto di quelle realmente necessarie al funzionamento corretto dellapplicazione (presenti nel target <jarGen>); servono solamente per evitare errori nella IDE.

1.1.1.16. Utilizzo delle classi stub nel codice dellapplicazione client. Il codice del programma client risiede nel main; ci sono tre variabili, che si suppone vengano valorizzate come parametri di lancio: host ossia il nome dellhost su cui risiede la web service, ci e cf, estremi dei codici libro tra i quali compiere la ricerca.
Per poter accedere ai metodi della web service, il client deve creare uno stub: stub = new LibreriaWSStub("http://"+host+":8081/axis2/services/LibreriaWS?wsdl"); ove stub di tipo stubLibWS.LibreriaWSStub, subtipo di org.apache.axis2.axis2.Stub, la classe creata tramite lutility WSDL2Java. Il costruttore riceve lindirizzo della web service, che nel mondo Axis2 rappresentata dal WSDL file7; qui si ipotizzato che Tomcat nel nodo host operi sulla porta 8081. Il client chiama il web method BooksDnListByRange, passando i codici ci e cf, e si aspetta in ritorno un array di record di tipo LibroDn; il metodo public LibroDn[] BooksDnListByRange(int ci,int cf), cos dichiarato nella classe LibreriaWS. Tuttavia la chiamata del metodo mediante lo stub non una semplice invocazione, come in Corba, ma unoperazione pi complessa, che segue le strutture del file WSDL: BooksDnListByRange req = new BooksDnListByRange(); req.setArgs0(ci); req.setArgs1(cf); BooksDnListByRangeResponse resp = stub.booksDnListByRange(req); LibroDn[] ldn = resp.get_return(); Il client alloca un oggetto richiesta, di tipo BooksDnListByRange, definito anchesso, come pure i seguenti, entro la classe LibreriaWSStub (che un contenitore di svariate classi oltre a se stessa). Se sono previsti parametri di passaggio, come in questo caso, essi sono campi interni alloggetto richiesta e possono essere settati usando gli appositi metodi che loggetto fornisce (cio setArgs0 e setArgs1). Nel caso il web method preveda un oggetto di ritorno, come in questo caso, allora la chiamata al metodo via stub restituisce un oggetto risposta, di tipo BooksDnListByRangeResponse; loggetto risposta contiene al suo utilizzare. 7 A differenza dei servizi Windows e delle web service ASMX, non viene fornita uninterfaccia direttamente accessibile da browser, ce esponga i web methods.

32/41

Riflessioni libere
interno un membro di tipo LibroDn[], che proprio ci che interessa al client, accessibile mediante il metodo get_return. Il resto del codice dellapplicazione LibreriaWSClient non presenta interesse particolare; in ultimo, uscendo dallapplicazione, bene invocare stub.cleanup per liberare tutte le risorse, ma non mandatario.

1.1.1.17. Build dellapplicazione client. Il target <compile> di build.xml si limita a compilare sia le classi applicative (Client) sia le classi generate a partire dal file WSDL (LibreriaWSStub). Il target <jarGen> che genera il file LibreriaWSClient.jar include la seguente lista di librerie del framework axis2: <zipfileset src="${axis2.libs}/axis2-adb-1.6.1.jar"/> <zipfileset src="${axis2.libs}/axis2-kernel-1.6.1.jar"/> <zipfileset src="${axis2.libs}/axis2-transport-local-1.6.1.jar"/> <zipfileset src="${axis2.libs}/axis2-transport-http-1.6.1.jar"/> <zipfileset src="${axis2.libs}/axiom-api-1.2.12.jar"/> <zipfileset src="${axis2.libs}/axiom-impl-1.2.12.jar"/> <zipfileset src="${axis2.libs}/commons-logging-1.1.1.jar"/> <zipfileset src="${axis2.libs}/commons-httpclient-3.1.jar"/> <zipfileset src="${axis2.libs}/commons-codec-1.3.jar"/> <zipfileset src="${axis2.libs}/httpcore-4.0.jar"/> <zipfileset src="${axis2.libs}/mail-1.4.jar"/> <zipfileset src="${axis2.libs}/neethi-3.0.1.jar"/> <zipfileset src="${axis2.libs}/wsdl4j-1.6.2.jar"/> <zipfileset src="${axis2.libs}/XmlSchema-1.4.7.jar"/>
Queste 14 librerie devono essere sempre disponibili ad ogni applicazione client di una web service Axis2, non importa quanto semplice sia lapplicazione client.

Costruzione della jax-ws web service LibreriaWSNew.


Come LibreriaWS, anche la web service LibreriaWSNew stata costruita usando Eclipse Indigo, seguendo la metodologia bottomup; per nel caso di LibreriaWSNew non stato utilizzato il framework Axis2, ma un framework parzialmente compreso in Java, cio jax-ws.

Preparazione del framework jax-ws.


Jax-ws RI (Reference Implementation) un framework che fornisce strumenti per sviluppare le web service e i loro client; il cuore del framework Metro di Glassfish, ma utilizzabile anche separatamente. Nonostante sia in parte integrato nei JDK, necessario scaricare JAX-WS<versione>.zip dal sito (parte del progetto Glassfish) http://jax-ws.java.net/<versione>, lultima versione disponibile 2.2.6. Scompattando lo zip sotto C:\, viene creata la directory C:\jaxws-ri; di particolare importanza la sottodirectory lib, contenente alcuni JAR file non presenti nel JDK: questi vanno integrati nellinstallazione di Tomcat, copiandoli in <Tomcat install dir>/shared/lib, bisogna inoltre settare la shared/lib nel file di configurazione catalina.properties, e riavviare poi Tomcat. Per semplicit, si pu usare lutility fornita da jaxws-ri: bisogna posizionarsi nella directory jaxws-ri con un command prompt e dare il comando ant install (che agisce sul command file build.xml presente nella directory), con Tomcat attivo8. Prima di utilizzare il build file, bisogna definire due variabili dambiente: CATALINA_HOME, che identifica la directory di installazione di Tomcat, e JAXWS_HOME che punta alla root della directory dinstallazione di jaxws, quindi JAXWS_HOME = C:\jaxws-ri.

Se la versione di JDK 1.7, non serve altro; se per si utilizza JDK 1.6, bisogna dare anche il comando ant install-api, che compie le seguenti azioni: crea la directory endorsed entro <JDK root>/jre/lib e vi copia dentro i due file jaxws-api.jar e jaxb-api.jar; crea la directory <Tomcat root>/endorsed e vi copia gli stessi due file. Tutto ci nasce dal fatto che Java 1.6 include jaxws-api.jar e jaxb-api.jar versione 2.1, mentre jaxws-ri utilizza le versioni 2.2; per far s che Java utilizzi le versioni desiderate, si ricorre al meccanismo Java Endorsed Standards Override Mechanism. Un discorso analogo vale per la creazione della directory endorsed entro Tomcat, per se si utilizza Tomcat 7 e non Tomcat 6 (con JDK 1.6), tale directory pu essere eliminata senza problemi.

33/41

Riflessioni libere

Impostazione del progetto Eclipse LibreriaWSNew.


stato utilizzato un Java project, di nome LibreriaWSNew; creo il package srv, e allinterno di srv creo la classe web service, LibreriaImpl. Questa classe va segnata con lannotazione seguente: @WebService // annotation class, javax.jws.WebService public class LibreriaImpl { @WebMethod public String[] SystemInfo() { ... } } Il framework jax-ws utilizza ampiamente le annotazioni, contenute in jsr181-api.jar; in particolare la classe che implementa una web service devessere obbligatoriamente contraddistinta con lannotazione @WebService; lannotazione ammette diversi parametri, che se, come nel caso presente, non vengono settati, assumono valori default (largomento verr approfondito nel seguito). Lanotazione WebMethod non obbligatoria: anche se omessa, un metodo della classe web service che sia pubblico diventa un web method, cio compare nel WSDL ed richiamabile dai client9; anchessa ammette parametrizzazione. Anche il tipo di ritorno e i parametri di passaggio di un web method possono essere annotati, rispettivamente con @WebResult e @WebParam; qui non viene fatto, si vedr un esempio duso di @WebParam nel seguito. La classe web service non ha bisogno di alcun main, infatti ad istanziare la classe ci pensa lapplication server (Tomcat). La scrittura della classe web service sembra completa; bisogna fare il build dellapplicazione: ci che verr generato un WAR file. Le fasi pi caratteristiche sono lannotation processing e la creazione del WAR file; sono precedute dalle solite fasi <clean> e <init>, e seguite dal target <copy>. Il target <init> crea la solita directory build, con le sottodirectory classes e war, questultima destinata a ospitare il WAR file.

Fase di annotation processing.


La fase successiva a <init> implementata dal target <build-service>:: <property name="lib.home" value="${env.JAXWS_HOME}/lib"/> <property name="srcdir" value="${basedir}/src"/> <path id="jaxws.classpath"> <pathelement location="${env.JAVA_HOME}/lib/tools.jar"/> <fileset dir="${lib.home}"> <include name="*.jar"/> </fileset> </path> <taskdef name="annotationProcessing" classname="com.sun.tools.ws.ant.AnnotationProcessingTask"> <classpath refid="jaxws.classpath"/> </taskdef> <target name="build-service"> <annotationProcessing includeantruntime="false"
9

Proprio lannotazione @WebMethod pu essere usata per evitare che un metodo pubblico della classe web service divenga un web method richiamabile dai client. Sia M un metodo della classe web service: posso trovare utile dichiararlo pubblico per esigenze di programmazione interne allapplicazione web service, ma non voglio che divenga un web method accessibile da parte dei client. Come fare ? La soluzione lutilizzo dellannotazione @WebMethod opportunamente parametrizzata: @WebMethod(exclude=true) public tipoRet M(pars) il default dellattributo exclude false.

34/41

Riflessioni libere
fork="true" verbose="${verbose}" destdir="${classes}" srcdir="${srcdir}" sourceDestDir="/" procOnly="false"> <classpath> <path refid="jaxws.classpath"/> <pathelement location="${srcdir}"/> </classpath> </annotationProcessing> </target> il processing delle annotazioni in realt comprende anche la compilazione di tutti i sorgenti. La classe AnnotationProcessingTask compresa in jaxws-tools.jar. Se lesecuzione del target <build-service> avviene correttamente, entro build/classes trovo i file .class di tutti i sorgenti, qui trovo build/classes/srv/LibreriaImpl.class; inoltre compare anche la sottodirectory build/classes/srv/jaxws che contiene per ogni web method una coppia di classi, una per la richiesta e una per la risposta: nel caso corrente, essendoci il solo web method SystemInfo, la sottodirectory build/classes/srv/jaxws contiene due sole classi, SystemInfo.class e SystemInfoResponse.class. Notare: lesecuzione del target <build-service> ha come side effect la creazione della directory srv/jaxws, vuota, allo stesso livello della directory build; non chiaro come evitare il verificarsi di questo effetto sgradito, cos ho imposto la cancellazione a posteriori della directory spuria: <target name="build-service"> <annotationProcessing ... </annotationProcessing> <delete dir="${basedir}/srv" includeEmptyDirs="true"/> </target>

Fase di creazione del WAR file.


Il target <create-war> crea il WAR file:
<target name="create-war"> <war warfile="${wardir}/jaxws-${ant.project.name}.war" webxml="etc/web.xml"> <webinf dir="${basedir}/etc" includes="sun-jaxws.xml"/> <zipfileset dir="${basedir}/etc" includes="*.wsdl,*.xsd" prefix="WEB-INF/wsdl"/> <classes dir="${classes}"/> </war> </target>

Il nome del WAR file deciso dallattributo warfile, che stabilisce sia la directory in cui viene crato il file (cio build/war) sia il suo nome, che risulta essere jaxws-libreria essendo libreria il nome del progetto: <project basedir="." default="main" name="libreria">. Un WAR file un JAR file che deve soddisfare alcuni requisiti, sia di struttura che di contenuto: deve contenere la directory WEB-INF; la directory WEB-INF a sua volta deve contenere il file descrittivo web.xml, inoltre le classi (file .class) presenti devono stare nella sottodirectory classes, i JAR file presenti devono stare nella sottodirectory lib. Oltre alla directory WEB-INF, di norma presente la directory META-INF (allo stesso livello di WEB-INF), contenente il file manifest MANIFEST.MF (senza nulla di particolare). In particolare, questo tipo di WAR ha bisogno, oltre alle classi contenute in bluid/classes e a web.xml, di un altro file descrittivo, cio sun-jaxws.xml. Allo scopo, entro la basedir del progetto ho creato il folder etc (allo stesso livello di build e src), contenente i file descrittivi (web.xml e sun-jaxws.xml): luso della directory etc una scelta non obbligata, ma convenzionalmente seguita, nelle JAX-WS web service. Eventuali file WSDL e XSD dovrebbero stare nella directory etc e verrebbero copiati entro un omonimo folder del WAR file; qui non sono per presenti, per cui il tag <zipfileset> entro <war> non ha azione.

1.1.1.18.

Connessione al database.

35/41

Riflessioni libere
La web service ha altri metodi oltre SystemInfo, e alcuni di questi utilizzano Oracle; potrei porre ojdbc6.jar entro la sottodirectory lib di WEB-INF, aggiungendo al target <create-war> la riga seguente:
<webinf dir="C:\oraclexe\Drivers" includes="ojdbc6.jar" prefix="WEB-INF/lib"/>

Cos facendo, entro il WAR file compare la sottodirectory lib contenente il file ojdbc6.jar (non scompattato nelle sue singole classi). Lapplicazione risultante funziona poi correttamente, tuttavia qui ho seguito unaltra strada: supponendo che il database server e il web application server (Oracle e Tomcat) stiano sulla stessa macchina, pongo ojdbc6.jar entro la directory shared/lib di Tomcat, cos non ho bisogno di porre alcun database driver entro i singoli WAR file.

Installazione.
Larchivio jaxws-libreria.war, che copio nella directory WAR Files per comodit, la JAX-WS web service; viene caricata come un qualsiasi WAR file, ad esempio usando la funzionalit di WAR upload propria di Tomcat Manager. Cos facendo, compare in Tomcat webapps il sito jaxws-libreria: la web service pronta. Attraverso i log di Tomcat, si pu verificare facilmente che gi stata creata unistanza della classe LibreriaImpl.

Impostazione del progetto Eclipse LibreriaWSNewClient.


La scrittura di un client della web service un po pi semplice e intuitiva di quanto lo sia nel caso Axis2, inoltre il client per funzionare non ha bisogno di librerie apposite, sufficiente il runtime di Java a patto che non utilizzi funzionalit avanzate e quindi non coperte dallimplementazione nativa in Java di JAX-WS. Il client di LibreriaWSNew, LibreriaWSNewClient, unapplicazione console; la classe principale Client, contenuta in Client.java entro il package app; il package app contiene anche una classe utilit, Uti (implementata in Uti.java), per gestire linput da tastiera. Il build dellapplicazione genera LibreriaWSNewClient.jar. Le prime fasi del build sono i soliti target <clean> e <init> (questultima genera la solita sottodirectory build/classes); seguono poi la fase di generazione dello stub, la fase di compilazione, infine la fase di generazione del JAR file e la sua copia in ExeJar.

Fase di generazione dello stub.


La fase di generazione dello stub implementata dal target <wsimport>: <property name="stubPkg" value="stubLibWS"/> <property name="wsdlSource" value="http://localhost:8081/jaxws-libreria/Libreria?wsdl"/> <path id="jaxws.classpath"> <pathelement location="${env.JAVA_HOME}/lib/tools.jar"/> </path> <target name="wsimport" description="Stub generation"> <java classname="com.sun.tools.internal.ws.WsImport" fork="true"> <classpath refid="jaxws.classpath"/> <arg value="-keep"/> <arg value="-d"/> <arg value="${basedir}/src"/> <arg value="-p"/> <arg value="${stubPkg}"/> <arg value="${wsdlSource}"/> </java> </target> La classe WsImport, appartenente al package com.sun.tools.internal.ws, si trova in tools.jar, libreria facente parte del JDK (ma non del JRE). La parametrizzazione dice alla classe di utilizzare il file WSDL di LibreriaWSNew, inoltre stabilisce che le classi generate apparterranno al package stubLibWS e che il package star nella directory src.

36/41

Riflessioni libere
La classe WsImport svolge un ruolo analogo a quello svolto dalla classe WSDL2Java nel framework Axis2; i risultati sono molto simili, per sono atomizzati ossia distinti in pi classi. Il package stubLibWS contiene una coppia di classi per ciascun web method, ad esempio SystemInfo e SystemInfoResponse, pi altre classi, tuttavia le classi di diretto interesse nella scrittura del client sono linterfaccia LibreriaImpl e la classe LibreriaImplService, subtipo di Service. Service, contenuta in rt.jar, la classe che rappresenta in generale il client di una web service; il subtipo LibreriaImplService mappato sulla specifica web service LibreriaWSNew mediante una annotazione (generata da WsImport; anche la classe WebServiceClient appartiene a rt.jar):
@WebServiceClient(name="LibreriaImplService",

targetNamespace="http://srv/",

wsdlLocation="http://localhost:8081/jaxwslibreria/Libreria?wsdl") public class LibreriaImplService extends Service

{ }

. . .

1.1.2. Utilizzo dello stub nel codice client.


La fase di generazione dello stub implementata dal target <wsimport>:

xxx

37/41

Riflessioni libere

Inbound message server at 1333614596966 MessageContext.HTTP_REQUEST_METHOD=POST accept: application/soap+xml, multipart/related connection: keep-alive content-length: 171 content-type: application/soap+xml; charset=utf-8;action="http://srv/LibreriaImpl/SystemInfoRequest" host: ETSGEMDSK365:8081 user-agent: JAX-WS RI 2.2.6b21 svn-revision#12959 <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"><S:Header/><S:Body><ns2:SystemInfo xmlns:ns2="http://srv/"/></S:Body></S:Envelope> Outbound message server at 1333614607122 MessageContext.HTTP_RESPONSE_CODE=0 MessageContext.HTTP_RESPONSE_HEADERS is null <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"><S:Body><ns2:SystemInfoResponse xmlns:ns2="http://srv/"><return>Windows XP v5.1 arch. x86</return><return>User = NT AUTHORITY/SYSTEM</return><return>home=C:\WINDOWS\system32\config\systemprofile</return><return> currDir=C:\Programmi\Apache Software Foundation\Tomcat 7.0</return><return>JAVA_HOME=C:\Programmi\Java\jdk1.6.0_29</return><return>JAXWS_HOME=c:\jaxws -ri</return></ns2:SystemInfoResponse></S:Body></S:Envelope> Inbound message server at 1333617554254

38/41

Riflessioni libere
MessageContext.HTTP_REQUEST_METHOD=POST accept: application/soap+xml, multipart/related connection: keep-alive content-length: 171 content-type: application/soap+xml; charset=utf-8;action="http://srv/LibreriaImpl/SystemInfoRequest" host: 10.16.106.92:8081 user-agent: JAX-WS RI 2.2.6b21 svn-revision#12959 <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"><S:Header/><S:Body><ns2:SystemInfo xmlns:ns2="http://srv/"/></S:Body></S:Envelope> Outbound message server at 1333617607723 MessageContext.HTTP_RESPONSE_CODE=0 MessageContext.HTTP_RESPONSE_HEADERS is null <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope"><S:Body><ns2:SystemInfoResponse xmlns:ns2="http://srv/"><return>Windows XP v5.1 arch. x86</return><return>User = NT AUTHORITY/SYSTEM</return><return>home=C:\WINDOWS\system32\config\systemprofile</return><return> currDir=C:\Programmi\Apache Software Foundation\Tomcat 7.0</return><return>JAVA_HOME=C:\Programmi\Java\jdk1.6.0_29</return><return>JAXWS_HOME=c:\jaxws -ri</return></ns2:SystemInfoResponse></S:Body></S:Envelope>

Gestione degli eventi in Corba.


Gli eventi Corba sono eventi per loro natura remoti; gli eventi Corba sono realizzati mediante il paradigma push-pull, di natura generale, non specifico del mondo Corba. Si parla di oggetti pusher e di oggetti puller: i primi spediscono dati e i secondi li ricevono, senza che ci sia in generale alcun sincronismo, n interazione diretta, tra chi spedisce e chi riceve; il meccanismo di trasporto dei dati chiamato canale: il concetto di canale molto importante nella gestione degli eventi Corba, perch la sola condizione affinch oggetti pusher e puller possano comunicare che utilizzino lo stesso canale.

Generalit su oggetti pusher e puller in Corba.


Un oggetto pusher spedisce dati su un canale: non sa quanti e quali saranno i consumer dei dati, non sa nemmeno se esistano, loggetto pusher si rapporta solamente con il canale. Per poter operare, dispone dellinfrastruttura, appartenente al framework Corba, chiamata event management; le principali componenti dellevent management di Orbacus sono lapplicazione event service e linsieme delle classi dedicate alla gestione degli eventi. Tra le altre cose, levent management permette la gestione completa dei canali. Una volta che il pusher ha scritto i dati sul canale, se ne disinteressa. Si noti che il pusher non spedisce pi copie dei dati, una per client, ma effettua ununica spedizione/scrittura sul canale. Gli oggetti puller, ossia i consumer, si pongono in ascolto sul canale: pi client, eterogenei o meno, fruiscono dellunico pacchetto di dati inviati dal pusher. Anche gli oggetti puller sono ignari dei pusher, e si rapportano unicamente con il canale su cui sono in ascolto: il concetto di canale centrale.

Lapplicazione Event Service di Orbacus.


La documentazione Orbacus suggerisce luso della classe com.ooc.CosEvent.Server, che si trova in OBEvent.jar, e il sorgente JOB-4.3.4/event/src/com/ooc/CosEvent/Server.java. Ho modificato il file Server.java per consentire il passaggio della porta e per permettere la terminazione guidata del servizio, analogamente a quanto gi fatto con lapplicazione name service. Nel caso della porta, gestisco la nuova opzione p (ossia --port) seguita dal valore numerico; il default, utilizzato se non viene specificata lopzione, la porta 14000. Per la terminazione guidata del servizio, rimando al paragrafo analogo esaminato a proposito del name service: anche per event service ho utilizzato la classe Taskent e un timer, cambia solo il nome del file di alt, che nel caso dellevent service DeathEVS.dat, sempre creato in Orbacus/lib. Il lancio dellevent service avviene tramite il command file Events.bat, in Orbacus/lib:
java -Xbootclasspath/p:"C:\Orbacus\lib\OB.jar;C:\Orbacus\lib\OBEvent.jar" com.ooc.CosEvent.Server %1 %2 %3 %4 %5

39/41

Riflessioni libere
i parametri opzionali permettono il passaggio di opzioni come il passaggio di una porta specifica. Lapplicazione crea loggetto DefaultEventChannelFactory, e servendosi della factory crea il canale DefaultEventChannel, a disposizione degli oggetti pusher e puller; in generale gli oggetti utilizzano canali propri, creati servendosi della default factory. Gli oggetti pusher e puller si possono collegare allevent service usando un apposito file di configurazione, BootEVS.dat, oppure mediante luso di properties programmatiche, analogamente a quanto gi visto per il collegamento al name service da parte di oggetti client e server.

Funzionamento di applicazioni pusher e puller astratte.


Un oggetto pusher non vive di vita autonoma, fa parte di unapplicazione pi grande che pu essere definita applicazione pusher; analogamente, lapplicazione che ospita un oggetto puller unapplicazione puller. Ipotizzo che gli oggetti pusher e puller, quindi le applicazioni ospite, utilizzino levent service, attivo su una macchina nota (tramite nome DNS o indirizzo IP) e su una porta assegnata, anchessa nota. Lapplicazione pusher deve poter svolgere le seguenti operazioni: 1) istanziare gli oggetti ORB e POA 2) connettersi allevent service 3) istanziare gli oggetti pusher 4) far operare ciascun oggetto pusher, in modo che possa acquisire i dati e scatenare gli eventi ossia inviare dati sul canale di sua competenza 5) terminare gli oggetti pusher 6) terminare correttamente loggetto ORB, quando lapplicazione cessa. Nella mia implementazione, il canale viene creato e infine distrutto dalloggetto pusher che lo utilizza; non lunica scelta possibile. Lapplicazione puller deve poter svolgere le seguenti operazioni: I) istanziare loggetto ORB II) connettersi allevent service III) collegarsi al canale di competenza IV) gestire i dati di evento tramite un proprio event handler V) rilasciare il canale quando non ha pi intenzione di gestire i dati di evento VI) terminare correttamente loggetto ORB, quando lapplicazione cessa. Le applicazioni, sia pusher sia puller, devono poter compiere le operazioni suddette interfacciandosi con la libreria MyCorbaWorld, e nientaltro; e linterfacciamento devessere di alto livello, cio pusher e puller non devono preoccuparsi dei dettagli Corba. La mia implementazione soddisfa pienamente i requisiti di disaccoppiamento tra pusher e puller, evitando ogni forma di sincronismo o comunicazione diretta tra i due: ciascuno di essi usa il canale su cui viaggiano i dati di evento, senza preoccuparsi che ci siano o meno le controparti.

Visione dinsieme della mia architettura.


Espongo nel seguito la mia implementazione della gestione degli eventi Corba: si tratta di unarchitettura completa, in quanto copre ogni aspetto della gestione degli eventi Corba, eccezion fatta per gli eventi tipati. basata su alcune applicazioni e classi che vanno viste come prototipi, cio pur non essendo del tutto generali possono essere immediatamente riutilizzate per costruire oggetti analoghi. Lo start e lo stop dellapplicazione event service sono gi stati illustrati. Come prototipo di applicazione pusher, considero lapplicazione PusherPeopleApp: questa istanzia loggetto PusherPeople, che spedisce dati tratti dal database DBPeople sul canale PeopleEventChannel.

Alcune ipotesi restrittive.


Come gi detto, ipotizzo che la creazione e la distruzione dei canali siano svolte dagli oggetti pusher; ci implica che gli oggetti puller possano solo porsi in ascolto sui canali, quindi questi devono essere in qualche

40/41

Riflessioni libere
modo preesistenti (in altri termini, se il canale non stato ancora creato, lapplicazione puller utente non pu far altro che ritentare in seguito lascolto). I canali non sono permanenti: se levent service termina, alla riattivazione tutti i canali preesistenti non esistono pi; lunica eccezione DefaultEventChannel, che sempre disponibile in quanto viene creato ad ogni partenza dellevent service. Si potrebbe decidere di far creare alcuni canali alla partenza dellevent service, cos come viene creato DefaultEventChannel: in questo modo sia gli oggetti pusher sia gli oggetti puller dovrebbero limitarsi a collegarsi ai canali preesistenti, sempre disponibili. Ho per scelto in modo diverso, e i canali vengono creati e distrutti ogni volta. Esiste una pluralit di oggetti pusher, tutti derivanti da ununica classe astratta, Pusher; invece c un unico oggetto puller, implementato dalla classe Puller, per tutte le applicazioni puller. Per quanto possibile, il lavoro di basso livello svolto dalla libreria MyCorbaWorld: perci la classe GestoreCorba stata arricchita di nuovi metodi, inoltre stata affiancata da altre classi, come Pusher e Puller. Anche gli specifici oggetti pusher, e tutti i tipi utilizzati dalle applicazioni (derivanti dai file IDL), fanno parte della libreria MyCorbaWorld. Le applicazioni pusher non vedono la classe Pusher, vedono solamente i suoi subtipi; le applicazioni puller non vedono la classe Puller, ma solo metodi assai generali di inizio e fine ricezione degli eventi forniti dalla classe GestoreCorba. Come ipotesi di lavoro, evito agli oggetti pusher, per quanto possibile, le operazioni di reperimento diretto dei dati di evento: gli oggetti pusher sono client Corba degli oggetti server. Ad esempio, PusherPeople chiama i metodi delloggetto ServerPeople, perci PusherPeopleApp va lanciata dopo aver istanziato loggetto ServerPeople, quindi dopo aver lanciato GenericCorbaHost. Siccome i canali sono di importanza centrale, e vengono creati dagli oggetti pusher, posso supporre che le applicazioni pusher partano logicamente per prime.

Descrizione di unapplicazione pusher: PusherPeopleApp.


Suppongo di aver gi lanciato GenericCorbaHost e istanziato ServerPeople, allo start di PusherPeopleApp. Siccome PusherPeopleApp unapplicazione pusher prototipale, essa segue il ciclo nominale.

1.1.2.1. Impostazione del progetto. Lapplicazione PusherPeopleApp stata sviluppata mediante un Java project, applicazione desktop di tipo grafica, per la grafica utilizza Swing. Nel build path c la sola libreria MyCorbaWorld.jar. PusherPeopleApp consiste di un solo file sorgente, MainDlg.java nel package app; la classe MainDlg stata creata mediante il wizard NewOtherWindow BuilderSwing DesignerApplication Window.
La classe MainDlg ha alcuni membri privati di interesse: v) private static String NShost = null e private static int NSdoor = 0, rispettivamente nome del computer e porta sui quali trovare il servizio di naming, che si suppone siano passati come parametri di lancio vi) GestoreCorba gestCorba (inizialmente null) Il main assegna, se presenti, args[0] e args[1] rispettivamente a NShost e NSdoor, quindi istanzia la classe Lapplicazione crea utilizza il metodo InitStuff d XxxPusherPeopleApp usa la InitStuff versione client perch non istanzia alcun oggetto server,ma non mandatario xxx Xxx GenericCorbaHost fa solo la lista dei canali xxx

41/41

You might also like