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
|