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.
|