MokaByte 61 - Marzo 2002 
Web Services
parte VI: Java API for XML Messaging (JAXM)
di
Massimiliano Bigatti
Elemento fondamentale della strategia per i Web Services di SUN, le JAXM, ad oggi rilasciate in versione 1.0.1 Early Access, consentono di implementare nella propria applicazione comunicazioni basate su XML e conformi a standard quali SOAP, ebXML ed altri.Introduzione

Introduzione
Nello spazio dedicato ai Web Services di Mokabyte settembre 2001, abbiamo affrontato lo stato dell'arte della piattaforma Java ed abbiamo visto le sue tecnologie chiave orientate ad XML ed ai servizi Web: JAXM, JAX-RPC, JAXR, JAXB, JAXP. Fortunatamente le consonanti dell'alfabeto sono quasi finite :-)
Scherzi a parte probabilmente in un futuro ci abitueremo a trattare tutte queste tecnologie come "J2EE-WS" o qualche cosa del genere.
Questo mese ci concentreremo su JAXM (Java API for XML Messaging). Come i più attenti ricorderanno, nel corso degli articoli precedenti abbiamo già parlato di JAXM ed abbiamo spiegato come questa tecnologia sia una sorta di API per la messaggistica XML. La novità rilevante é che, nel frattempo, la SUN ha rilasciato la versione 1.0.1 final delle specifiche ed una implementazione di riferimento 1.0.1 Early Access.
La disponibilità di una versione 1.0, seppur in forma di beta, completa di documentazione ed esempi é pur sempre un punto di partenza abbastanza solido per poter cominciare a sperimentare seriamente con la tecnologia.
Il materiale é scaricabile dal sito http://java.sun.com/products/xml/jaxm.
Grazie a questo rilascio possiamo passare al lato pratico e presentare un esempio di utilizzo delle API JAXM.

 

Scaricare ed installare JAXM
JAXM é fornito da SUN in un unico download, denominato Java XML Pack in versione Winter 01. Questo download contiene:

  • JAXM 1.0.1 ea1
  • JAXP 1.2 ea1
  • JAXR 1.0 ea
  • JAX-RPC 1.0 ea1

Ciascuna API é completa di JAR, documentazione di base, esempi e strumenti di supporto. Per molte di queste tecnologie vengono forniti anche file WAR (Web Archive File) per applicazioni web di esempio o gestione.
E' consigliabile scaricare, oltre al codice, anche la documentazione.
Oltre al JAX-Pack é necessario, per provare gli esempi di JAXM anche avere il web container Tomcat, in versione 3.2.3 - o meglio - in versione 4.0.1.
Inoltre viene consigliato l'utilizzo di Ant.
Nota: se desiderate un download unico di tutto questo materiale, più qualche altro elemento aggiuntivo, la SUN ha reso disponibile anche il Java Web Services Developer Pack che riunisce appunto tutto in un unico download.
Una volta scaricati questi componenti é possibile passare all'installazione.
Per quanto riguarda Tomcat, in funzione del sistema operativo utilizzato sarà necessario effettuare le necessarie operazioni di installazione. Ad esempio, io per le prove ho utilizzato Mac OS X che, come noto, é basato su Unix ed in particolare su una variante di BSD. Mac OS X ha Java2 integrato con il sistema operativo ed in particolare é dotato della versione 1.3.1 che non ha rivelato problemi di incompatibilità con Tomcat 4.0.1. Una volta definite le variabili JAVA_HOME e CATALINA_HOME é stato possibile lanciare Tomcat tramite lo script startup.sh. In ambiente Windows si dovrà fare uso del batch startup.bat.
Una volta ottenuto il funzionamento di Tomcat, é possibile passare all'installazione di JAXM. Secondo le istruzioni é necessario copiare i file JAR della directory lib di JAXM nella common/lib di Tomcat. Inoltre é necessario copiare i file WAR della directory samples di JAXM nella webapps di Tomcat.
Stando alle istruzioni, per concludere l'installazione si dovrà poi copiare i file jaxm-client.jar in common/lib e jaxm-provider.war in webapps. In realtà per utilizzare la web application jaxm-provideradmin (consente di cambiare i profili per i server JAXM - vedi Figura 1) sarà necessario installare in common/lib anche xalan.jar (che si potrà trovare nella lib di JAXP) e che fornisce il supporto alle trasformazioni XSLT che evidentemente sono utilizzati da jaxm-provideradmin.
Per testare il corretto funzionamento di JAXM è possibile utilizzare la web application jaxm-simple, facendo puntare il browser a: http://127.0.0.1:8080/jaxm-simple.


Figura 1 - Lo strumento di amministrazione JAXM in azione

 

Uno sguardo alle API
Le API di JAXM sono contenute all'interno di due packages:

  • javax.xml.messaging
  • javax.xml.soap.

Il primo fornisce le API per implementare la comunicazione one-way e cioé l'invio di un messaggio da un provider (la sorgente) ad un consumer (il destinatario), oppure di tipo request-response (composto da una richiesta ed una risposta).
Il secondo package contiene sostanzialmente il modello ad oggetti per le componenti SOAP; qui si trovano anche classi per la gestione degli Attachments che sono supportati da JAXM anche se non sono parte integrante delle specifiche SOAP ma solo una estensione proposta in W3C tra gli altri, proprio da SUN. Gli attachment rivestono una particolare importanza per JAXM in quanto sono indispensabili in ebXML.
Una nota su ebXML: il livello di trasporto utilizzato da questo standard é sempre SOAP ma a questo richiede siano aggiunti gli attachment e siano formalizzate alcune informazioni ad esempio nell'header. Inoltre, SOAP puro e semplice non entra mai nel merito del contenuto del messaggio. Per comunicare con sistemi ebXML invece, é necessario strutturare le informazioni in una specifica struttura. JAXM viene in aiuto in quanto consente la definizione di "profili" che permettono l'utilizzo di SOAP in modo semplice o per seguire lo standard ebXML.
Un altro particolare salta all'occhio: come ormai consuetudine, le connessioni - in questo caso a provider SOAP - non vengono eseguite con una instanziazione di oggetti ma tramite una factory dedicata. Questo approccio, utilizzato ad esempio anche nelle JCA (Java Connector Architecture) o in JDBC (Java Data Base Connectivity), consente di svincolare il codice client dalle specifiche implementazioni delle API JAXM.

 

Il supporto a SOAP
Come si é detto, JAXM fornisce un modello ad oggetti per la definizione dei messaggi in formato SOAP.
Come é possibile creare messaggi SOAP utilizzando JAXM? Per prima cosa é necessario partire dalla factory dei messaggi per crearne una nuova:

MessageFactory mf = MessageFactory.newInstance();

Una volta ottenula la factory, é possibile creare nuovi messaggi:

SOAPMessage msg = mf.createMessage();

Il metodo createMessage() si occupa di definire un nuovo messaggio vuoto costituito da un blocco principale (la "part") a cui opzionalmente sarà possibile aggiungere una o più attachments.
Per valorizzare correttamente il messaggio, é necessario accedere all'envelope, passando dalla "part":

SOAPPart sp = msg.getSOAPPart();
SOAPEnvelope envelope = sp.getEnvelope();

A questo punto é possibile ottenere i riferimenti alla header ed al body:

SOAPHeader hdr = envelope.getHeader();
SOAPBody bdy = envelope.getBody();

I due tipi SOAPHeader e SOAPBody sono interfacce che derivano da SOAPElement, a sua volta questa interfaccia deriva da Node che rappresenta sostanzialmente un Nodo XML con possibilità di manipolazioni ridotte. Questo infatti non é lo stesso Node presente in JAXP anche se rappresenta lo stesso concetto, lo fa modellandone meno attributi ed azioni.
Una volta ottenuto l'oggetto Body é possibile aggiungere ulteriori elementi. Ad esempio, nella web application jaxm-simple fornita con JAXM si trova:

gltp = bdy.addBodyElement(envelope.createName("GetLastTradePrice",
                          "ztrade","http://wombat.ztrade.com"));

che produce un elemento XML:


<ztrade:GetLastTradePrice xmlns:ztrade="http://wombat.ztrade.com">

Per valorizzare un elemento XML é necessario poi aggiungergli un elemento figlio di tipo TEXT, come ad esempio:

gltp.addChildElement(envelope.createName("symbol",
          "ztrade","http://wombat.ztrade.com")).addTextNode("SUNW");

Il documento XML prodotto dal codice visto sopra é il seguente:

<soap-env:Envelope xmlns:soap-env = "http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header />
<soap-env:Body>
<ztrade:GetLastTradePrice xmlns:ztrade="http://wombat.ztrade.com">
<ztrade:symbol>SUNW</ztrade:symbol>
</ztrade:GetLastTradePrice>
</soap-env:Body>
</soap-env:Envelope>


Una volta acquisita la conscenza del funzionamento dell'object model di JAXM che modella messaggi SOAP, é possibile implementare chiamate a servizi diversi che possono richiedere anche elementi aggiuntivi rispetto all'esempio precedente (ad esempio namespaces aggiuntivi od encoding SOAP).

 

Un sinonimo per...
Per approfondire JAXM ho sviluppato una semplice web application che utilizza queste API per chiamare un servizio su Internet per l'accesso ad un dizionario di sinonimi. La web application "jaxm-prova" utilizza come base il codice di "jaxm-simple" ma é composta da una sola servlet invece che da due.
In particolare la struttura prevede un file principale (index.html) che implementa una FORM per l'inserimento della parola di cui ricercare il sinonimo (attenzione: il servizio Internet funziona con parole in inglese e per la verità non é molto completo, ma sufficiente per provare in pratica la comunicazione SOAP). La FORM effettua una POST sulla servlet SendingServlet che si occupa di eseguire l'interazione con il web service e di ritornare l'elenco dei sinonimi trovati.
Una particolarità di questo servizio web é che all'interno del valore di risposta viene restituito un blocco HTML contenente l'elenco dei sinonimi ed un disclaimer che fornisce alcuni avvertimenti all'utente.
Un'altra particolarità é la necessità di utilizzare diversi namespaces aggiuntivi nel messaggio SOAP, per indicare l'uso di Schema XML ed encoding SOAP. Un esempio di messaggio di chiamata é:

<?xml version="1.0" ?>
<soap-env:Envelope
xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap-env:Header />
<soap-env:Body>
<ns1:getServiceResponsePublic
xmlns:ns1="urn:MyBubble-SoapServices"
soap-env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<serviceName xsi:type="xsd:string">Synonym</serviceName>
<inputText xsi:type="xsd:string">Mouse</inputText>

</ns1:getServiceResponsePublic>
</soap-env:Body>
</soap-env:Envelope>


Come si può vedere, il codice é in qualche modo differente rispetto all'esempio precedente. Per indicare a JAXM di generare un messaggio SOAP come questo é necessario agire via programma con una serie di chiamate.
Per aggiungere i namespace é necessario effettuare la chiamata addNamespaceDeclaration().
Nel nostro caso:

String xsi = "http://www.w3.org/2001/XMLSchema-instance";
env.addNamespaceDeclaration("xsi", xsi);
env.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema");
javax.xml.soap.Name xsiTypeString = env.createName("type", "xsi", xsi);

La variabile xsiTypeString viene creata perché il namespace "xsi" viene utilizzato anche nei tag "serviceName" ed "inputText" e quindi verrà utilizzata più volte in seguito. Il Name creato avrà come nome del namespace "xsi" e come urn il valore della stringa xsi.
La creazione del body element viene poi effettuata come in jaxm-simple con l'unica differenza della chiamata setEncodingStyle().

SOAPBodyElement gltp = bdy.addBodyElement(env.createName("getServiceResponsePublic",
                   "ns1",
                   "urn:MyBubble-SoapServices"));
gltp.setEncodingStyle("http://schemas.xmlsoap.org/soap/encoding/");

Questo metodo sembra però non sortire particolari effetti in quanto si rende poi necessario, per i tag "serviceName" ed "inputText", specificare esplicitamente l'attributo che in SOAP encoding specifica il tipo di dato (si veda [3] per ulteriori dettagli sull'encoding SOAP):

SOAPElement e1 = gltp.addChildElement(
env.createName("serviceName")).addTextNode("Synonym");
e 1.addAttribute( xsiTypeString, "xsd:string" );
SOAPElement e2 = gltp.addChildElement(
                      env.createName("inputText")).addTextNode(
                      req.getParameter("parola"));
e2.addAttribute( xsiTypeString, "xsd:string" );

L'aggiunta dell'attributo "type" viene eseguito da JAXM in modo intelligente: essendo già presente la definizione del namespace a livello di Envelope, nel messaggio SOAP non viene ripetuta la dichiarazione xmlns:xsi in quanto vale quella presente a livello più alto.

 

Invio del messaggio
A questo punto la creazione del messaggio SOAP é conclusa e si può passare all'invio tramite HTTP. Per fare ciò é necessario dichiarare un "Endpoint" e cioé una destinazione fisica per l'invio del messaggio. Nel caso del servizio sinonimi scelto, il destinatario é il server mybubble.com, sulla porta 8080 (l'implementazione é basata infatti su Apache SOAP).

URLEndpoint urlEndpoint = new URLEndpoint("http://www.mybubble.com:8080/soap/servlet/rpcrouter");
SOAPMessage reply = con.call(msg, urlEndpoint);

La chiamata avviene tramite il metodo call() presente nella classe SOAPConnection. Se tutto funziona correttamente, il servizio ritorna una risposta che viene rappresentata da JAXM come oggetto SOAPMessage. A questo punto é possibile navigare la struttura del messaggio per ottenere le funzioni di interesse.

sp = reply.getSOAPPart();
env = sp.getEnvelope();
bdy = env.getBody();

//Ottiene getServiceResponsePublicResponse
Iterator childs = bdy.getChildElements();
SOAPElement e = (SOAPElement)childs.next();

//Ottiene return xsi:type="xsd:string"
childs = e.getChildElements();
e = (SOAPElement)childs.next();

//Ottiene il nodo testo
childs = e.getChildElements();
Text text = (Text)childs.next();
result = text.getValue();

Il metodo utilizzato per l'estrazione del valore di ritorno (result) non é sicuro: si basa sul fatto che il formato di risposta combaci con quello aspettato. La gestione del messaggio di risposta é sicuramente un'area in cui sarebbe necessario sviluppare ulteriormente jaxm-prova.

 

Conclusioni
JAXM 1.0 é una API a basso livello per la gestione di messaggi SOAP e solo di questi (il supporto ad ebXML si basa sul fatto che quest'ultimo utilizza SOAP per la comunicazione). JAXM avrebbe dovuto però essere una API per un generico messaging XML, quello che si può riscontrare nelle API é invece una forte propensione a SOAP - le API avrebbero potuto chiamarsi Java API for SOAP messaging (JASM). Questa limitazione, unita al fatto che i servizi per il messaging vero e proprio, che inizialmente dovevano essere di cinque tipologie diverse, consentono solo un "fire and forget" ed un "request-response", fanno di JAXM un'API che sicuramente ha bisogno di ulteriori sviluppi da parte della comunità Java. Questo anche in considerazione del fatto che altri package SOAP per Java, come ad esempio Apache-SOAP (anche se non più in evoluzione), risultano essere più avanzati di JAXM. Dalla sua JAXM ha il vantaggio di essere l'API "ufficiale" e quindi di fornire una certa sicurezza per gli investimenti di codifica.
La giovinezza di JAXM é anche forse il risultato della "corsa" di SUN per salire sul carrozzone dei Web Services ed anche se con API ufficiali la piattaforma non é completamente evoluta, Java rimane comunque la via che moltissimi sviluppatori scelgono (ricerca di Giga Group - pubblicata da ToolNews) per implementare i loro Web Services.

 

Bibliografia
[1] Massimiliano Bigatti - "Web Services (4) - Lo stato dell'arte della piattaforma Java", Mokabyte, Settembre 2001
[2] Andrea Giovannini - "SOAP e Java: Integrazione di applicazioni con Java e il nuovo protocollo SOAP" , Mokabyte, Gennaio 2001
[3] Specifiche SOAP - http:/www.w3c.org/TR/SOAP


Massimiliano Bigatti è SUN Certified Enterprise Architect for Java Platform, Enterprise Edition Technology. Si occupa di architetture applicative basate su Java ed Internet, technical writing e musica rock

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it