MokaByte 81 - Gennaio 2004 
JWS FAQ
10 Domande e risposte sui Web Service
di
Massimiliano Bigatti
Forse per la limitazione della documentazione a disposizione degli sviluppatori, o forse per il fatto che alcune operazioni sono più complesse di altre, mi capita spesso di vedere domande ricorrenti su determinati aspetti dei Web Services e delle tecnologie Java relative. Queste sono le risposte.

1. Quali sono le tecnologie Java per i Web Services?
La panoramica delle tecnologie che sono state aggiunti negli ultimi anni alla piattaforma Java è rappresentata in figura 1.


Figura 1 - Panoramica delle tecnologie Java


Queste tecnologie hanno il seguente scopo:

  • JAXM (Java API for XML Messaging). Implementazione della messaggistica SOAP a basso livello. Approfondimento;
  • SAAJ (SOAP with Attachments API for Java). Implementazione del modello informativo degli elementi di SOAP. Approfondimento;
  • JAX-RPC (Java API for XML Remote Procedure Call). Supporto client e server allo sviluppo rapido di servizi Web basati su SOAP (e non solo) e WSDL con un modello di sviluppo simile ad RMI. Approfondimento;
  • JAXR (Java API for XML Registries). Supporto a registri di servizi Web come UDDI e ebXML R. Approfondimento;


3. In un messaggio SOAP: cosa metto nell'header e cosa nel body?
Come noto, un messaggio SOAP può essere composto da diversi elementi, ed i due principali sono l'intestazione ed il corpo. La differenza tra queste due sezioni è semantica: servono a rendere più chiaro lo scopo di una determinata informazione; se questa è contenuta nell'intestazione è un meta-dato di contorno; se è contenuta nel corpo, è una informazione applicativa. Ad esempio:

Header Body
id dell'utente
token di sicurezza
id della transazione in corso
indirizzo ip del chiamante
dati applicativi
parametri del servizio da chiamare



4. Che differenza c'è tra Axis e JAXM?
JAXM è lo standard Java per la messaggistica SOAP, mentre Axis è una implementazione di SOAP per Java con un parziale supporto a JAXM di Apache Group. Axis sta già migrando a JAXM ed attualmente la supporta in parte. E' consigliabile sicuramente di codificare per JAXM, che è una interfaccia indipendente dall'implementazione, un servizio che verrà messo a disposizione delle applicazioni da parte dei container, come ad esempio gli application server come WebSphere, o lo stesso Axis.
Le due tecnologie sono complementari: JAXM è l'interfaccia, mentre Axis è (parzialmente) una implementazione.

 

5. Quando usare JAXM e quando usare JAX-RPC?
Entrambe le API di SUN implementano SOAP, quindi si potrebbe pensare che l'utilizzo, o l'approfondimento, delle due sia indifferente. In realtà non è così: JAXM e JAX-RPC non sono una ripetizione della stessa tecnologia con nomi diversi.
Per prima cosa, entrambe utilizzano SAAJ come implementazione del modello informativo di SOAP, ma il loro scopo è differente.

  • JAXM fornisce un controllo fine sul messaggio (tramite SAAJ), consentendo di manipolare ciascun elemento della richiesta ed accedere a qualsiasi elemento della risposta SOAP. Viene utilizzato in soluzioni di messaggistica, come ad esempio: l'invio asincrono di informazioni real-time, l'interfacciamento con servizi strutturati in modo particolare (non standard), collegamento a servizi che hanno una risposta dinamica (cambia la struttura al variare della richiesta o dei dati ritornati);
  • JAX-RPC implementa invece un generico sistema di RPC su XML; la tecnologia non è vincolata a SOAP, ma aperta ad eventuali evoluzioni future. E' invece basata fortemente su WSDL, che utilizza per implementare la mappatura XML->Java e viceversa. Con JAX-RPC non è necessario codificare a basso livello, se si dispone di un WSDL, si possono generare le classi Java che implementano il client ed il server. La stessa cosa può avvenire a partire da una interfaccia che estende java.rmi.Remote, in modo similare al funzionamento di RMI e di rmic.


JAX-RPC è dunque più indicato per sviluppare applicazioni complesse, con molti servizi, e per integrare Web Service esistenti e completamente standard, quando non sia necessario entrare nel dettaglio tecnico della struttura dei messaggi SOAP di richiesta e risposta. Con JAX-RPC un servizio Web viene acceduto come se fosse un oggetto Java locale.

 

6. Come faccio a manipolare l'intestazione HTTP durante l'invio di un messaggio SOAP?
Con JAXM è possibile intervenire in modo fine su tutto il messaggio, manipolando intestazione, corpo ed allegati. E' possibile inserire anche nuovi elementi nell'intestazione HTTP, anche se, poco intuitivamente, è necessario farlo attraverso le intestazioni MIME. Inserire una intestazione HTTP aggiuntiva può servire a diversi scopi, oltre che per fornire indicazioni aggiuntive al server, ma anche solo per aggiungere la famigerata chiave SOAPAction che, sebbene spesso ignorata da molti servizi Web, in alcuni casi è obbligatoria. La sua mancanza può quindi impedire la risposta da parte del server, anche quando il messaggio SOAP nella sua integrità sia corretto. Per un esempio si veda sotto: è prima necessario ottenere l'oggetto MimeHeaders dal messaggio SOAP creato per poi impostare l'header voluta con setHeader() e poi ricordarsi di salvare le modifiche con saveChanges().

//SOAPMessage msg;
//...
MimeHeaders mh = msg.getMimeHeaders();
mh.setHeader("SOAPAction", "http://tempuri.org/DailyDilbertImage");
msg.saveChanges()

 

7. Come faccio ad avere il controllo sull'invio di un messaggio SOAP?
Alcune volte può rendersi necessario inviare un messaggio SOAP con un protocollo diverso da HTTP, l'unico ad oggi supportato da JAXM, oppure intervenire nell'invio del messaggio per pilotare caratteristiche di dettaglio del meccanismo di invio, che in SOAP avviene sempre, se su HTTP, con una operazione di POST.
E' possibile utilizzare le Apache Commons, una libreria di funzioni basilari comuni per tutti i progetti di Apache Group, che include una classe per l'invio tramite HTTP POST: PostMethod. Questa implementa l'invio tramite HTTP 1.0 o 1.1, ad esempio, il metodo seguente implementa una chiamata simile a quella presente nella classe JAXM SOAPConnection utilizzata per inviare un messaggio SOAP:
public String call( SOAPMessage request, Object to ) throws SOAPException {
String result = null;

try {
  URL url = new URL( to.toString() );
  String host = url.getHost();
  int port = url.getPort();

  PostMethod method = new PostMethod( url.getFile() );
  method.setRequestHeader("Content-type", "text/xml");

  ByteArrayOutputStream out = new ByteArrayOutputStream();
  request.writeTo( out );

  String requestBody = new String( out.toByteArray(), "UTF-8" );

  method.setRequestBody( requestBody );
  method.execute( new HttpState(), new HttpConnection( host, port ) );
  int returnCode = method.getStatusCode();

  byte[] body = method.getResponseBody();
  result = new String( body, "UTF-8" );

} catch( Exception ex ) {
  ex.printStackTrace();
}

return result;
}

La classe PostMethod implementa il meccanismo di POST HTTP, e con questa realizzare una chiamata SOAP. I metodi salienti, ed utilizzati nell'esempio sopra riportato sono:

  • setRequestHeader(). Aggiunge una chiave alle intestazioni HTTP, ad esempio Content-Type, che nel caso di una richiesta SOAP 1.1 deve essere impostata text/xml;
  • setRequestBody(). Imposta la stringa da utilizzare come corpo della richiesta;
  • execute(). Esegue la richiesta, utilizzando un oggetto HttpState, che consente anche di eseguire una serie di richieste collegate tra loro;
  • getResponseBody(). Ritorna una stringa con la risposta ottenuta dal Web Server.



8. Come posso inviare e ricevere allegati binari?
La specifica SOAP w/attachments, supportata sia da JAXM che da JAX-RPC, consente di inviare allegati binari a messaggi SOAP, utilizzando il protocollo MIME. Questa funzionalità può essere impiegata ad esempio per implementare un servizio di memorizzazione di immagini, dove il messaggio SOAP indica i metadati sull'immagine da inserire e l'allegato binario definisce l'immagine da memorizzare; un'altra possibilità è quella di passare certificati digitali binari al fine di eseguire una comunicazione dove il client sia certificato.
Ad ogni modo, in un messaggio con allegati inviato tramite http, la prima parte MIME contiene il messaggio SOAP vero e proprio, formattato in XML secondo le consuete regole; le parti aggiuntive contengono i documenti binari da inviare. Per allegare un documento è necessario utilizzare il framework di Java per la gestione espandibile di contenuti binari, il Java Activation Framework. Di questo viene utilizzata la classe DataHandler:

URL url = new URL("file:///export/files/pic1.jpg");
DataHandler dataHandler = new DataHandler( url );
AttachmentPart att = message.createAttachmentPart( dataHandler );
att.setContentId( "pic1" );
message.addAttachmentPart( att );


La classe AttachmentPart, parte di SAAJ, definisce un singolo allegato. Un oggetto di questo tipo viene creato dal metodo createAttachmentPart() della classe Message, che si aspetta come parametro l'oggetto DataHandler che punta al contenuto binario da allegare. L'oggetto AttachmentPart viene poi aggiunto al messaggio tramite il metodo addAttachmentPart().

 

9. E se l'allegato binario non è un allegato MIME ma è contenuto nel messaggio come blocco di dati codificato (p.e. base64)?
Nella domanda precedente si è visto come allegare contenuti binari ad un messaggio SOAP, secondo le specifiche SOAP w/attachment. Alcune volte però, sorge la necessità di interfacciare servizi Web che inviano allegati binari all'interno del messaggio, magari codificati in base64. Questa strada ha alcuni vantaggi, come la semplicità di sviluppo e di debug e l'occupazione di banda è sostanzialmente simile all'uso del protocollo SOAP w/attachment, visto che l'uso di MIME impone la codifica base64 degli allegati. Se un elemento XML contiene un blocco di dati base64, è possibile decodificarlo facilmente utilizzando Javamail. Ad esempio, si consideri questo estratto del programma Dilbert Client:

e = (SOAPElement)childs.next();
InputStream is = MimeUtility.decode(new ByteArrayInputStream(
                                    e.getValue().getBytes() ),"base64" );


L'oggetto ByteArrayInputStream crea un InputStream a partire dal contenuto del SOAPElement e, che contiene i dati in base64; il formato dei dati, insieme allo stream, vengono passati al metodo MimeUtility.decode(), presente nel package javax.mail.internet (deve essere presente nel CLASSPATH il jar di Javamail).
Una volta ottenuto lo stream dei dati, è possibile leggerlo e visualizzarlo, ad esempio con Swing, tramite la classe JLabel. Nella porzione di codice successiva, viene creato un semplice ciclo che legge le informazione dal flusso decodificato e le inserisce in un altro flusso, di tipo ByteArrayOutputStream. Questa classe consente poi di ritornare le informazioni come un array di byte, che può essere poi passato al costruttore di ImageIcon, la classe Swing che implementa una Icon attraverso una immagine in un formato riconosciuto dal sistema (come gif o jpeg):

ByteArrayOutputStream imageFile = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
while( true ) {
int size = is.read( buf );
if( size == -1 )
break;
imageFile.write( buf, 0, size );
}

label.setText("");
label.setIcon( new ImageIcon( imageFile.toByteArray() ) );

 

10. Devo implementare una soluzione per il problema X, ma quale standard scelgo?
Al posto della X si può inserire: sicurezza, transazioni, prestazioni, gestione... ed altro! Sono diversi gli aspetti per cui non si è ancora giunti ad un accordo sulle specifiche, o meglio, sono presenti più specifiche per risolvere lo stesso problema. Si consideri la sicurezza: esistono, tra le altre, le specifiche WS-Security e SAML. La prima è supportata da OASIS, ed è basata su standard di mercato, come HTTPS, SSL e X.509, ma richiederebbe specifiche di routing, per ora solo affrontate da standard creati da Microsoft e non condivisi con altri; SAML è invece una creazione di SUN, donata sempre ad OASIS e si basa su un concetto di asserzioni e di autorità che ne consentono o negano la validità.
Sulla scelta dello standard si possono prendere sostanzialmente tre strade: utilizzare uno degli standard presenti, inventarne uno nuovo, oppure non utilizzarne nessuno. Probabilmente la scelta strategicamente migliore è la prima: applicare uno standard esistente consente di risparmiare i tempi di sviluppo di specifiche alternative, e poi esiste una possibilità che lo standard adottato diventi in futuro quello di riferimento; utilizzando una specifica esistente, è anche possibile che produttori di software mettano a disposizione degli strumenti di supporto, oppure che il mondo dell'open source realizzi qualcosa al riguardo. Creare un proprio standard in certi casi può portare ad un risparmio immediato in termini di tempi di sviluppo, ma questo è in funzione della complessità del problema! In molti casi, studiare il materiale esistente ed applicarlo, risulta in un tempo inferiore a quello necessario ad ideare una nuova soluzione.

 

Conclusioni
In questo articolo sono state riportate alcune domande frequenti sulle tecnologie Java che ruotano attorno ai servizi Web, con le relative risposte. Avete ancora dei dubbi? Provate a postarli sul forum di Mokabyte! Forse qualcuno ha già trovato la risposta che cercate, e magari l'argomento di discussione potrebbe evolvere in un nuovo articolo.

 

Webliografia e riferiementi
[1] Bigatti -"Corso di Web Services, parte III: JAXM e SAAJ", http://www.mokabyte.it/2003/04/jws-3.htm
[2] Bigatti -"Corso di Web Services, parte V: JAX-RPC", http://www.mokabyte.it/2003/06/jws-5.htm
[3] Bigatti - "Using XPath with SOAP", http://webservices.xml.com/pub/a/ws/2003/09/16/jaxen.html

 
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