MokaByte 75 - Giugno 2003
Corso di Java Web Services
V parte: JAX-RPC
di
Massimiliano Bigatti

JAX-RPC
All'inizio del paragrafo "Perché WSDL", si è fatto cenno all'utilità di questo standard di descrizione dei servizi Web in congiunzione a strumenti di generazione di codice volto a supportare gli specifici servizi descritti nei documenti WSDL. JAX-RPC (Java API for XML Remote Procedure Call) contiene al suo interno proprio questa funzionalità, facendo leva su WSDL per semplificare l'accesso e la produzione di servizi Web. Piuttosto che API, infatti, JAX-RPC è più un insieme di regole e strumenti per nascondere allo sviluppatore i dettagli della comunicazione SOAP tra i diversi nodi.
Se da una parte JAXM e SAAJ forniscono un controllo fine sul messaggio SOAP, con la possibilità di intervenire su ogni aspetto di ciascun elemento, come il namespace e gli attributi, dall'altra questo aspetto comporta la necessità di scrivere molto codice per implementare ciascun elemento del messaggio, come la busta (Envelope), l'intestazione (Header) ed il corpo del messaggio (Body).
In contrapposizione, o meglio, a complemento di JAXM e SAAJ, è nata JAX-RPC. Questa tecnologia sta a JAXM come RMI sta alla comunicazione tramite Socket: semplifica la realizzazione di applicazioni distribuite basate su protocolli di comunicazione.
Potrebbe sembrare che JAXM e JAX-RPC si sovrappongano, in quanto entrambe implementano SOAP. In realtà, JAXM consente semplicemente di creare, consumare ed inviare messaggi SOAP, mentre JAX-RPC si occupa degli aspetti ad alto livello, e cioè di mappare interfacce e classi Java rendendoli disponibili come servizi Web. Inoltre non esiste un vincolo a SOAP: ad oggi è l'unico protocollo supportato, ma potrebbero aggiungersene di nuovi in futuro. In realtà, JAXM e SAAJ possono fornire a JAX-RPC l'implementazione del protocollo SOAP, ponendosi quindi come tecnologia sottostante e complementare alle API per l'RPC in XML. JAX-RPC 1.0 supporta SOAP 1.1, WSDL 1.1, SOAP con allegati e le estensioni MIME.

 

Architettura di JAX-RPC
JAX-RPC si basa su una architettura composta da codice intermedio che implementa la comunicazione a basso livello, lasciando a livello del programmatore solo le interfacce del servizio. In Figura 1 è presente un esempio, dove sia il client che il server utilizzano JAX-RPC per la comunicazione.


Figura 1
- Architettura di JAX-RPC

Il client dialoga con una interfaccia, generata da strumenti di sviluppo (in JWSDP è presente lo strumento xrpcc), prodotta a partire da un file WSDL. L'interfaccia è implementata da una classe che implementa i dettagli della comunicazione SOAP, lo stub. Al di sotto di questo, il runtime di JAX-RPC si occupa della comunicazione tramite SOAP/HTTP con il tie, il corrispondente dello stub sul lato server. E' poi compito del tie di comunicare con la reale implementazione del servizio.
E' però possibile utilizzare xrpcc per generare solo uno degli strati: quello client o quello server. Per creare uno stub è sufficiente avere a disposizione un file WSDL: dato in input allo strumento xrpcc, produce tutto il codice necessario per comunicare con il servizio. Il client non deve far altro che importare il package relativo al codice generato e deciso prima dallo sviluppatore ed instnziare la classe che rappresenta il servizio.
Per generare il lato server, è possibile partire, oltre che dal documento WSDL, anche da una interfaccia Java che estenda java.rmi.Remote e dove ciascun metodo sollevi l'eccezione java.rmi.RemoteException. A partire da questa xrpcc è in grado di generare i tie ed il documento WSDL di descrizione, oltre agli altri file necessari per installare il servizio in un Web Container.

 

Mappatura dei tipi
Nell'implementare la corrispondenza biunivoca tra WSDL e Java, JAX-RPC deve basarsi su un preciso standard che indichi in che modo un determinato tipo, presente in uno dei due mondi, debba essere rappresentato nell'altro. JAX-RPC dispone di una mappatura dei dati primitivi (Tabella 1), ma anche degli array, delle strutture dati complesse e delle enumerazioni.


Tabella 1
- Mappatura dati primitivi tra WSDL e Java


Se da una parte i dati primitivi trovano spesso diretta corrispondenza, e gli array WSDL possono essere mappati direttamente in array Java, le strutture dati complesse e le enumerazioni WSDL richiedono più lavoro. In questi casi vengono generate dagli strumenti di sviluppo classi apposite. Nel primo caso vengono generati componenti Javabeans strutturati secondo il modello presente nel documento WSDL.

 

Modelli di interazione
Nonostante WSDL definisca quattro diversi modelli di interazione tra il client ed il servizio, JAX-RPC supporta solo l'oneway e la request-response, più uno diverso, detto invocazione non-bloccante. Quest'ultimo è una variante della modalità request-response e prevede l'invio di un messaggio di richiesta e la ricezione di un messaggio di risposta, ma nel tempo che intercorre tra la ricezione della richiesta e la produzione della risposta, il client è svincolato dall'attesa. Questa modalità è disponibile però solo all'interno di container J2EE, dove è il server a poter rimanere in attesa della risposta al posto del client.

 

Un client JAX-RPC
Per creare un programma client con JAX-RPC è necessario partire dal file WSDL di definizione del servizio ed utilizzare lo strumento xrpcc per creare il codice di supporto, ad esempio con il comando seguente:

xrpcc.bat -d classes -s src -client -keep config.xml

Lo strumento supporta molte opzioni, in questo caso gli è stato indicato di posizionare le classi generate nella cartella classes, i sorgenti nella cartella src e di generare i soli stub (opzione "-client"). Inoltre, con l'opzione "-keep", non vengono cancellati i file intermedi, come i sorgenti del codice di infrastruttura generato. L'ultimo parametro del comando è il file di configurazione di xrpcc che indica cosa generare e contiene il riferimento al file WSDL da analizzare. Lo strumento xrpcc ed il formato del file di configurazione non sono parte dello specifiche JAX-RPC e dunque possono essere diversi in implementazioni diverse di questo standard. Il contenuto del file config.xml è presente nel listato 1.

Listato 1 - File di configurazione per xrpcc
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location="
file:///d:/max/codice/jax-rpc/client/CurrencyExchangeService.xml"
packageName="currencyExchange"/>
</configuration>

Gli elementi interessanti del file di configurazione sono l'attributo location che indica l'URL del WSDL sorgente (il percorso dovrà riflettere la reale posizione del file su disco) ed il nome del package in cui posizionare il codice. Lo strumento xrpcc, infatti, a fronte di un documento WSDL anche semplice, genera molte classi, ed è dunque buona norma racchiuderle in un package dedicato.
In questo caso è stato generato lo stub per l'accesso al servizio di cambio valute di XMethods. Analizzando il codice generato, si può notare la presenza dell'interfacca CurrencyExchangeService che rappresenta il servizio Web contenuto nel file WSDL. L'implementazione concreta di questa interfaccia è data dalla classe CurrencyExchangeService_Impl, che può essere instanziata direttamente. L'interfaccia dispone di un sigolo metodo per ottenere lo stub dell'unica operazione disponibile nel servizio, quella di cambio valute. Il metodo getCurrencyExchangePort() ritorna infatti un oggetto di tipo CurrencyExchangePortType che a sua volta è dotato del metodo getRate(String, String), rappresentazione Java della singola operazione effettuabile da questo servizio Web.
Nel listato 2 è presente il codice completo del client. Come si può notare, è molto più compatto dell'equivalente JAXM.

Listato 2 - ExchangeClient2.java
import java.rmi.*;
import javax.xml.rpc.*;

import currencyExchange.*;

class ExchangeClient2 {

public ExchangeClient2() {
sendMessage( "us", "euro" );
}

protected void sendMessage( String divisaIniziale, String divisaFinale ) {
try {
CurrencyExchangeService_Impl service = new CurrencyExchangeService_Impl();
CurrencyExchangePortType portType = service.getCurrencyExchangePort();

String endpoint = System.getProperty("endpoint");
if( endpoint != null ) {
((Stub)portType)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, endpoint);
}

System.out.println( portType.getRate( divisaIniziale, divisaFinale ) );
} catch( RemoteException re ) {
re.printStackTrace();
}
}

public static void main( String[] args ) {
new ExchangeClient2();
}
}

Si osservi la gestione dell'endpoint: l'indirizzo fisico del servizio viene inserito direttamente nel codice generato da parte dello strumento xrpcc, in quanto presente nel file WSDL. Potrebbe essere necessario però puntare allo stesso servizio in posizioni differenti. Per fare ciò è necessario impostare la proprietà ENDPOINT_ADDRESS_PROPERTY dello stub.
In ExchangeClient2.java, l'endpoint viene prelevato da una proprietà di sistema:
String endpoint = System.getProperty("endpoint");

Se questa non è nulla (la proprietà è stata effettivamente passata alla Virtual Machine), viene passata all'oggetto port tramite il metodo _setProperty():

if( endpoint != null ) {
((Stub)portType)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, endpoint);
}

L'oggetto port può essere convertito in un oggetto di tipo Stub, in quanto tutti gli oggetti di questo tipo implementano questa interfaccia.
Anche per questo esempio è presente nel codice del capitolo il relativo file di compilazione ed esecuzione per Ant. In questo caso è solo necessario modificare il file per indicare il percorso di installazione del JWSDP e lanciare il comando Ant senza parametri. L'esempio verrà compilato ed eseguito. Il file di build contiene inoltre la proprietà endpoint per puntare alla servlet JAXM che implementa questo servizio. Se non viene indicata nessuna proprietà nel file di build, il client accede al servizio di XMethods su Internet.

 

Realizzare un servizio
Per creare un servizio Web con JAX-RPC è possibile partire, oltre che dal WSDL, anche da una interfaccia che implementi java.rmi.Remote. In questo esempio viene replicato il servizio di cambio valute, utilizzando però, come punto di partenza un'interfaccia Java (Listato 3).

Listato 3 - CuurrencyExchange.java
package galacticExchange;

import java.rmi.*;

public interface CurrencyExchange extends Remote {
public float getRate( String country1, String country2 ) throws RemoteException;
}

Come si nota dal codice, il servizio è contenuto nel package galacticExchange: questo è infatti un servizio di fantasia, che ha la medesima struttura di quello di XMethods, ma che vorrebbe trattare di valute "intergalattiche". L'implementazione completa è presente nel Listato 4: come si nota non ci sono riferimenti a XML e tantomeno a SOAP.

Listato 4 - CurrencyExchangeImpl.java
package galacticExchange;

import java.rmi.*;

public class CurrencyExchangeImpl implements CurrencyExchange {
public float getRate( String country1, String country2 ) throws RemoteException {
return (float)20.10;
}
}

Come si nota osservando il codice dell'implementazione del servizio, questo ritorna una semplice costante, in modo similare a quello prodotto con JAXM.
Una volta compilato il codice per installare il servizio all'interno di un Web Container, è necessario creare un file WAR con il codice compilato ed un file xml di descrizione, jaxrpc-ri.xml. Quest'ultimo è specifico dell'implementazione di JAX-RPC del JWSDP e contiene informazioni relative ai nomi dei servizi, agli url da assumere all'interno del container, ed alle classi che implementano i servizi. Nel Listato 5 è presente il contenuto del file jaxrpc-ri.xml utilizzato in questo esempio.

Listato 5 - File jaxrpc-ri.xml per il servizio di cambio valute galattiche
<?xml version="1.0" encoding="UTF-8"?>

<webServices
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
version="1.0"
targetNamespaceBase="http://schemas.mokabyte.it/wsdl"
typeNamespaceBase="http://schemas.mokabyte.it/types"
urlPatternBase="/ws">

<endpoint
name="GalacticCurrencyExchangeEndpoint"
displayName="GalacticCurrencyExchange"
description=""
interface="galacticExchange.CurrencyExchange"
implementation="galacticExchange.CurrencyExchangeImpl"/>

<endpointMapping
endpointName="GalacticCurrencyExchangeEndpoint"
urlPattern="/galactic"/>

</webServices>

Una volta in possesso dell'interfaccia, dell'implementazione e del file di descrizione, è possibile passare alla fase di creazione del servizio. Con JWSDP è necessario utilizzare il comando:

wsdeploy.bat -o <nuovo file> <vecchio file>

WSDeploy si occupa di estrarre le informazioni dal war, creare i ties dei servizi, creare i file di supporto necessari e di ricompattare tutto all'interno di un nuovo file WAR, che potrà essere poi installato in un Web Container. Tutte queste operazioni possono essere fatte in automatico da Ant tramite il file di build presente nel codice del capitolo.
Una volta installato il servizio, è possibile analizzarne la struttura puntando all'indirizzo indicato nel file jaxrpc-ri.xml. In questo caso, all'URL http://127.0.0.1/mokabook_ws2/galactic è possibile accedere alla visualizzazione degli endpoint del servizio (Figura 2) e visualizzare il file WSDL del servizio.


Figura 2
- Visualizzazione degli endpoint del servizio GalacticExchange
(clicca sull'immagine per ingrandire)

Per provare il servizio è presente il programma ExchangeClient3.java: la sua struttura è identica ad ExchangeClient2.java, con la sola differenza che il nome del servizio puntato è diverso (e di conseguenza diverse le interfacce Java che fungono da proxy). Il listato completo di ExchangeClient3.java è presente nel codice del capitolo.

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