Capire l'ambiente JAX-RPC SI, Parte 2
di Arun Gupta
Feb 2004
traduzione di Alessio Cervellin
Ott 2004
Nella prima
parte si sono viste le potenzialità del wscompile
e
del wsdeploy
ed i file di configurazione utilizzati da questi tool.
In questa seconda ed ultima parte verranno
illustrati gli scenari più comuni che si possono presentare durante il
ciclo di sviluppo, deploy ed invocazione di un Web Service e come
poterli affrontare utilizzando il wscompile
e il wsdeploy
.
La Figura 1 mostra i cinque scenari più comuni che verranno esaminati
dettagliatamente in seguito.

Scenario # |
Cosa si ha
|
Cosa si vuole
|
Scenario 1 |
Interfaccia dell'endpoint del
servizio e sua implementazione |
Deployare il servizio ed
esporre un WSDL di tipo doc/lit (o rpc/lit
o rpc/enc ); il Web Service deve essere WS-I compliant
|
Scenario 2 |
URL del WSDL |
Un client che invoca il
servizio utilizzando degli static stub; Popolare gli header dal client
|
Scenario 3 |
URL del WSDL |
Un client che invoca il
servizio utilizzando un Dynamic Proxy |
Scenario 4 |
URL del WSDL |
Un client che invoca il
servizio utilizzando un Dynamic Invocation Interface (DII) |
Scenario 5 |
URL del WSDL |
Pubblicare il proprio
servizio;
Esporre lo stesso WSDL nel Web Service deployato
|
Ora si vedrà come utilizzare il
wscompile
e il wsdeploy
in questi casi d'uso,
per alcuni dei quali si definiranno una o più alternative per
raggiungere scopi leggermente differenti.
I file utilizzati dagli esempi sono i seguenti:
Il codice sorgente è disponibile per il download.
Scenario 1: Service Endpoint
Interface - Server
Obiettivo: Deployare il servizio ed esporre un WSDL di
tipo doc/lit
(rpc/lit
o rpc/enc
), a partire
dall'interfaccia dell'endpoint del servizio e le sue classi di
implementazione.
- Compilare l'interfaccia e le classi di implementazione
dell'endpoint del servizio.
- Generare un model file utilizzando il
wscompile
.
Dato che il
wscompile
si aspetta che sia specificata una tra le modalità
import, define o gen, utilizzare lo switch -define
come
modalità primaria e utilizzare lo switch -model
per
abilitare la generazione del model. Affinchè venga generato un WSDL di
tipo document/literal, utilizzare lo switch -f:documentliteral
.
Il comando è il seguente:
wscompile -define -f:documentliteral -d build/classes
-classpath build/classes -model model.xml.gz config.xml
dove config.xml
è il file di configurazione
a partire dall'interfaccia dell'endpoint del servizio. Ecco come sarà :
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<service name="StockQuoteService"
targetNamespace="http://stockquote.org/wsdl"
typeNamespace="http://stockquote.org/types"
packageName="com.example">
<interface name="com.example.StockQuoteProvider"
servantName="com.example.StockQuoteImpl"/>
</service>
</configuration>
Dato che veiene utilizzata la modalità -define
, verrà
generato un WSDL
con nome identificato dall'attributo service{name}
di
config.xml
, in questo caso StockQuoteService.wsdl
.
Questo WSDL è un'anteprima di ciò che verrà generato successivamente
in
fase di deploy dal wsdeploy
e reso disponibile come
parte del file WAR elaborato [cooked
WAR file].
-d build/classes
fa sì che i file .class
vengano generati nella cartella build/classes
la modalità -gen
può essere utilizzata al posto
della -define
, ma utilizzando la -define
rimanda la generazione degli artefatti non portabili [non-portable
artifacts] alla fase di deployment. Specificando lo switch -model
vengono raccolte nel model file tutte le informazioni riguardanti il
servizio, inclusi gli switch utilizzati dal wscompile
.
- Impacchettare in un WAR file, chiamato file WAR grezzo [raw
WAR file], l'interfaccia dell'endpoint del servizio, le classi di
implementazione, i value type associati, il model file generato e il
descrittore di deployment
. Pertanto le classi
StockQuoteProvider
, InvalidTickerException
e
StockQuoteImpl
del package com.example
, il
model.xml.gz
generato nel passo precedente e il descrittore di
deployment vengono raccolte insieme per costituire il WAR file grezzo [raw
WAR file]. Il descrittore di deployment utilizzato in questo caso è il
seguente:
<?xml version="1.0" encoding="UTF-8"?>
<webServices
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
version="1.0"
targetNamespaceBase="http://java.sun.com/xml/ns/jax-rpc/wsi/wsdl"
typeNamespaceBase="http://java.sun.com/xml/ns/jax-rpc/wsi/types"
urlPatternBase="/ws">
<endpoint
name="example"
displayName="Stock Example"
description="Stock Example Web Service endpoint"
interface="com.example.StockQuoteProvider"
implementation="com.example.StockQuoteImpl"
model="/WEB-INF/model.xml.gz"/>
<endpointMapping
endpointName="example"
urlPattern="/example"/>
</webServices>
La posizione del model file è specificata nell'attributo endpointType{model}
del descrittore di deployment. Tutti gli switch utilizzati dal wscompile
vengono memorizzati come parte del model e dunque disponibili al wsdeploy
per successive elaborazioni.
La struttura del WAR file grezzo [raw
WAR file] è la
seguente:

- Generare il file WAR elaborato [
cooked
WAR
file] utilizzando il wsdeploy
. Il comando è:
wsdeploy -o stock.war stock-raw.war
dove stock-raw.war
è il nome del file WAR grezzo [raw
WAR file] generato precedentemente. Il wsdeploy
elabora
il model specificato nel descrittore di deployment, generandone uno
internamente se non esiste, poi genera dal model vari artefatti
specifci dell'implementazione come i tie, i serializzatori, e i
runtime descriptor, assieme al WSDL di tipo document/literal.
Successivamente impacchetta tutti gli artefatti in un WAR file
elaborato [cooked
WAR file], chiamato stock.war
. Questo
file WAR può essere deployato
in un web container e l'endpoint può essere acceduto all'indirizzo
http://<host>:<port>/stock/<url-pattern>
dove
<url-pattern>
corrisponde all'attributo endpointMappingType{urlPattern}
di jaxrpc-ri.xml
(in questo caso vale http://localhost:8080/stock/example
).
La struttura di questo WAR è la seguente:

Il wsdeploy
elabora il descrittore di deployment
e il web.xml
contenuti nel WAR file grezzo [raw
WAR
file] e genera un nuovo descrittore di runtime, jaxrpc-ri-runtime.xml
,
e un web.xml
per il WAR file elaborato [cooked
WAR
file]. Include inoltre i jaxrpc-ri.xml
e web.xml
originali (contenuti nel WAR file grezzo) nel nuovo WAR
file rinominandoli jaxrpc-ri-before.xml
and web-before.xml
.
Il nuovo descrittore di runtime e il web.xml
vengono
utilizzati
dall'ambiente di runtime JAX-RPC per smistare le richieste alle
approriate combinazioni di tie/implementation.
StockQuoteProvider_Tie
è la classe tie generata.
InvalidTickerException
, StockQuoteImpl
,
StockQuoteInfo
e StockQuoteProvider
vengono copiate dal file WAR grezzo.
Tutte le altre classi
del
file
WAR elaborato vengono utilizzate per la serializzazione e
deserializzaazione dei messaggi di richiesta e risposta.
Viene infine generato un
WSDL document/ literal che può essere acceduto appendendo ?WSDL
all'indirizzo dell'endpoint. Il nome del WSDL viene scelto dal nome
dell'elemento <service>
specificato nel config.xml
.
Il comportamento predefinito del wsdeploy
è quello di
generare un WSDL di tipo
rpc/encoded. Tuttavia avendo specificato lo switch -f:documentliteral
al wscompile
che poi lo ha passato al wsdeploy
tramite il model, viene generato un WSDL di tipo document/literal
invece di
rpc/encoded.
Alt-1: Per generare e dunque esporre un WSDL
rpc/literal, sostituire lo switch -f:documentliteral
con
-f:rpcliteral
.
Alt-2: Per generare e dunque esporre un WSDL
rpc/encoded, non specificare lo switch -f:documentliteral
e per default sarà di tipo rpc/encoded.
Alt-3: Per generare un Web Service WS-I compliant,
aggiungere lo switch -f:wsi
insieme a -f:documentliteral
o -f:rpcliteral
.
Scenario 2: WSDL - Static Stubs Client
Obiettivo: Invocare un servizio tramite degli static
stub, a partire da un WSDL.
- Partire da un file di configurazione del
wscompile
basato su un WSDL di cui viene specificata la posizione. Tale file
sarà
del tipo:
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl
location="./StockQuoteService.wsdl"
packageName="com.example"/>
</configuration>
dove wsdl{location}
specifica la
posizione del WSDL (in questo caso si trova directory corrente). Al
wscompile
viene detto di generare gli artefatti nel package com.example
.
- Generare gli artefatti lato client utilizzando la modalità
-gen
del wscompile
. Il comando sarà del tipo:
wscompile -gen:client -d build/classes config.xml
dove config.xml
è il file di configurazione a partire da
un WSDL. Lo switch -gen:client
abilita la generazione
degli artefatti lato client. Tutti i file .class
vengono
generati nella directory build/classes
. Se l'attributo wsdl{location}
fa riferimento ad un WSDL situato su Internet, allora se si è dietro un
firewall potrebbe essere necessario specificare l'opzione -httpproxy
.
In questo caso il comando sarà :
wscompile -gen:client -d classes
-httpproxy:<host>:<port> config.xml
La porta del proxy è opzionale e di default è 8080. StockQuoteService.wsdl
viene esposto all' URL precedentemente specificato nel file di
configurazione e la lista degli artefatti generati è la seguente:
La classe StockQuoteService_Impl
è di particolare
interesse in quanto ha un accessor per ciascuna delle port esposte nel
WSDL (come definito nella sezione 4.3.11 delle specifiche JAX-RPC
1.1).
L'interfaccia di servizio generata viene utilizzata per la creazione di
istanze delle classi stub generate nel codice client.
StockQuoteProvider
è l'interfaccia dell'endpoint
del servizio generata ed è mappata nella port del WSDL. InvalidTickerException
e StockQuoteInfo
sono le classi utilizzate
dall'interfaccia dell'endpoint del servizio. StockQuoteProviderPort_Stub
è la classe stub generata. Le altre classi vengono utilizzate
dall'ambiente di runtime JAX-RPC per
la serializzazione e deserializzazione dei messaggi SOAP di richiesta e
risposta.
- Utilizzare la classe
StockQuoteService_Impl
generata per ottenere un riferimento a StockQuoteProvider
e invocare il Web Service. Il codice client sarà del tipo:
package com.example;
...
StockQuoteProvider port = (new
StockQuoteService_Impl()).getStockQuoteProviderPort();
float tradePrice = port.getLastTradePrice("SUNW");
Se il WSDL contiene il giusto indirizzo dell' endpoint menzionato per
la port
StockQuoteProviderPort
nel WSDL, il wscompile
genererà degli stub pre-configurati (ossia degli stub aventi
l'indirizzo dell'endpoint già correttamente settato). in caso
contrario
è necessario settare esplicitamente tale indirizzo negli stub generati:
import javax.xml.rpc.Stub;
...
StockQuoteProvider port = (new
StockQuoteService_Impl()).getStockQuoteProviderPort();
((Stub)port)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY,
YOUR_ENDPOINT_ADDRESS);
float tradePrice = port.getLastTradePrice("SUNW");
dove YOUR_ENDPOINT_ADDRESS
è il valore
corretto dell'indirizzo dell'endpoint.
Alt-1: Se lo stesso WSDL è esposto da un altro
endpoint, allora si possono riutilizzare sia gli artefatti generati che
il client e settare approriatamente da questo gli indirizzi degli
endpoint.
Alt-2: Se l'invocazione del Web Service richiede che
l'header venga popolato dal client con alcune informazioni, allora
bisogna specificare l'opzione -f:explicitcontext
quando si generano gli artefatti lato client. Ciascun header verrà
aggiunto come parametro nella signature del metodo. I valori ivi
memorizzati vengono passati negli header SOAP.
Scenario 3: WSDL - Dynamic Proxy
Client
Obiettivo: Invocare un
servizio utilizzando un dynamic proxy, a partire da un WSDL.
Questo tipo di invocazione andrebbe utilizzato solo quando il
WSDL contiene tipi di schema primitivi, altrimenti si raccomanda di
utilizzare il modello basato sugli static stub (scenario
2).
Un client di tipo Dynamic Proxy supporta l'interfaccia
dell'endpoint di servizio dinamicamente a runtime senza richiedere
alcuna generazione di codice per una classe stub. Il client ottiene
informazioni riguardanti il servizio semplicemente esaminando il WSDL.
Per maggiori dettagli si veda la sezione 8.2.3 delle specifiche
JAX-RPC 1.1.
Si faccia riferimento al Java Web Services Developer Pack
tutorial per un esempio
su come invocare un servizio utilizzando un dynamic proxy.
Scenario 4: WSDL - DII Client
Purpose: Invocare il servizio utilizzando la Dynamic
Invocation Interface, a partie da un WSDL.
Questo tipo di invocazione andrebbe utilizzato solo quando il
WSDL contiene tipi di schema primitivi, altrimenti si raccomanda di
utilizzare il modello basato sugli static stub (scenario
2).
Un client DII supporta l'invocazione dinamica di una
operazione sull'endpoint. Un client può invocare un metodo remoto anche
se la signature o il nome del servizio non sono noti a priori. Per
maggiori dettagli si faccia riferimento alla sezione 8.2.4 delle
specifiche JAX-RPC 1.1.
Si faccia riferimento al Java Web Services Developer Pack
tutorial per un esempio
su come invocare un servizio utilizzando DII.
Scenario 5: WSDL - Server
Purpose: Pubblicare un servizio, a partire da un WSDL.
- Specificare la posizione del WSDL nel file di
configurazione. Questo sarà dunque del tipo:
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl
location="http://example.com/stock?WSDL"
packageName="com.example" />
</configuration>
dove il valore dell'attributo wsdl{location}
(in questo caso http://example.com/stock?WSDL
) specifica
la posizione del WSDL. Inoltre si specifica di generare gli artefatti
nel package com.example
.
- Importare il WSDL per generare l'interfaccia dell'endpoint
del servizio ed i value types. Il comando è:
wscompile -import -d build/classes -keep -s build/src
-model build/model.xml.gz config.xml
-keep
fa sì che tutti i file .java
generati vengano conservati.
-d build/classes
fa sì che tutti file .class
vengano generati nella cartella build/classes
.
-s build/src
fa sì che tutti i file .java
generati vengano copiati nella cartella build/src
directory.
-model
fa sì che venga generato il model in build/model.xml.gz
.
StockQuoteService.wsdl
viene esposto all'URL specificato precedentemente nel file di
configurazione e la lista degli artefatti generati è la seguente:

StockQuoteProvider
è l'interfaccia dell'endpoint
generata, StockQuoteInfo
è il value type utilizzato
all'interno dell'interfaccia, e StockQuoteProvider_Impl
è
una classe di implementazione di template può essere utilizzata come
classe base per l'implementazione dell'endpoint del servizio. StockQuoteService.java
è l'interfaccia del servizio generata utilizzata per la creazione di
istanze delle classi stub generate. Una implementazione concreta di
questa classe viene generata se al posto dell'opzione -gen:client
viene utilizzata -import
. GetXXX.java
sono
classi di convenienza utilizzate dallo stub per la serializzazione dei
messaggi di input
e output
definiti nel WSDL.
- Scrivere il codice di implementazione dell'endpoint del
servizio utilizzando la sua interfaccia,
StockQuoteProvider.java
,
e i value
types, se presenti, generati al passo precedente.
- Ora che sono disponibili sia l'interfaccia che
l'implementazione dell'endpoint del servizio, seguire il passo 3 dello
scenario 1 per generare il WAR file grezzo [raw WAR]. Il descrittore di
deployment sarà il seguente:
<?xml version="1.0" encoding="UTF-8"?>
<webServices
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"
version="1.0"
targetNamespaceBase="http://java.sun.com/xml/ns/jax-rpc/wsi/wsdl"
typeNamespaceBase="http://java.sun.com/xml/ns/jax-rpc/wsi/types"
urlPatternBase="/ws">
<endpoint
name="example"
displayName="Stock Example"
description="Stock Example Web Service endpoint"
interface="com.example.StockQuoteProvider"
implementation="com.example.StockQuoteImpl"
model="/WEB-INF/model.xml.gz"
wsdl="/WEB-INF/StockQuoteService.wsdl"/>
<endpointMapping
endpointName="example"
urlPattern="/example"/>
</webServices>
Nota, l'attributo webServices{wsdl}
specifica la
posizione del WSDL e l'attributo webServices{model}
specifica la posizione del model file nel WAR grezzo [raw
WAR]. La struttura di questo WAR è la seguente:
- Seguire il passo 4 dello scenario 1 per generare il WAR
elaborato [cooked WAR]. Dato che il WSDL è già contenuto nel WAR
grezzo
[raw
WAR] e il descrittore di runtime fa
riferimento a questo WSDL, il wsdeploy
modifica il descrittore di runtime, jaxrpc-ri-runtime.xml
,
con il WSDL già esistente. La struttura del WAR file elaborato [cooked
WAR] è la seguente:
Notare che il model e il WSDL del raw
WAR vengono
copiati nel cooked
WAR.
GetLastTradePrice
, GetLastTradePriceResponse
, GetStockQuote
,
GetStockQuoteResponse
, InvalidTickerException
,
StockQuoteImpl
, StockQuoteInfo
, StockQuoteProvider
,
StockQuoteProvider_Impl
e StockQuoteService
vengono copiati dal raw WAR. StockQuoteProvider_Tie
è la
classe tie generata. Tutte le altre classi vengono utilizzate per la
serializzazione e deserializzazione dei messaggi di richiesta e
risposta.
Il wsdeploy
elabora il descrittore di deployment
e il web.xml
contenuti nel raw
WAR file e genera un nuovo descrittore di runtime, jaxrpc-ri-runtime.xml
,
e un web.xml
per il cooked
WAR. Include
inoltre i jaxrpc-ri.xml
e web.xml
originali (forniti dal raw
WAR) nel cooked WAR file
chiamandoli jaxrpc-ri-before.xml
e web-before.xml
.
Questi due nuovi file
vengono utilizzati dall'ambiente
JAX-RPC per smistare le richieste alle appropriate combinazioni di
tie/implementation.
Glossario
- Artifact [Artefatto]
- è una classe java, un file WSDL, un file model, o
qualsiasi altro file generato in automatico dal wscompile o wsdeploy.
- cooked WAR [WAR elaborato]
- E' un file WAR, generato dal wsdeploy, che può essere
deployato in un servlet Web Service di tipo JAX-RPC..
- Dynamic Proxy Client
- supporta l'interaccia dell'endpoint di un servizio
dinamicamente a
runtime senza richiedere alcuna generazione di codice della classe stub
che implementa l'interfaccia dell'endpoint del servizio. Il client
ottiene informazioni sul servizio solo esaminando il WSDL che lo
descrive. Per maggiori dettagli si faccia riferimento alla sezione
8.2.3 delle specifiche JAX-RPC 1.1.
- DII Client
- fornisce supporto per l'invocazione dinamica di
un'operazione sull'endpoint di un servizio. Un client può invocare un
metodo remoto anche se la signature o il nome del servizio non sono
noti a priori, Per maggiori dettagli si faccia riferimento alla sezione
8.2.4 delle specifiche JAX-RPC 1.1.
- implementation-specific [specifico
dell'implementazione]
- Specifico di una implementazione JAX-RPC e non raccomandato
dalle specifiche. Questa caratteristica lega l'applicazione sviluppata
ad una specifica implementazione di JAX-RPC e quindi potrebbe renderla
non portabile, ossia si hanno artefatti non portabili [non-portable
artifacts]
- Java Web Services Developer Pack
- Il Java Web Services Developer Pack (Java WSDP) è un set di
strumenti che può essere utilizzato per creare, testare e deployare
applicazioni XML, Web Service, e applicazioni Web con le più recenti
tecnologie ed implementazioni degli standard dei Web Service.
- Model
- Il model è una rappresentazione specifica di JAX-RPC SI
dell'endpoint o del client di un Web Service, generato dal wscompile,
generalmente disponibile sottoforma di file .xml.gz. Contiene tutte le
informazioni in quanto alle strutture dati interne che consentono più
facilmente la generazione degli artefatti specifci
dell'implementazione. Dato che il wsdeploy supporta solo un set
limitato di switch, la maggior parte di quelli specificati col
wscompile vengono memorizzati come parte del model.
- Non-portable artifacts [ artefatti non portabili]
- Sono gli artefatti con un mapping non definito nelle
specifiche JAX-RPC sicchè se vengono generati da multiple
implementazioni di JAX-RPC, potrebbero risultare diversi e sono stub,
tie
e serializzatori. Sono anche detti artefatti dipendenti dalla
specifica [implementation-specific artifacts].
- Portable artifacts [artefatti portabili]
- Sono artefatti con un ben definito mapping nella specifica
JAX-RPC sicchè se vengono generati da multiple implementazioni di
JAX-RPC, risulteranno uguali e sono gli endpoint del servizio, le
interfacce dell'endpoint, i value types, e le classi di eccezione.
- raw WAR [WAR grezzo]
- Un file WAR contenente un descrittore di deployment e gli
artefatti portabili/non portabili generati dal wscompile. Serve da
input per il wsdeploy.
- wscompile
- Strumento fornito dal JAX-RPC SI incluso nel Java Web
Services Developer Pack per la generazione di alcuni artefatti
richiesti dalla specifica JAX-RPC e altri artefatti specifici
dell'implementazione necessari per sviluppare/deployare/invocare un Web
Service.
- wsdeploy
- Strumento fornito dal JAX-RPC SI incluso nel Java Web
Services Developer Pack per ottenere un WAR elaborato [cooked WAR] a
partire da un WAR grezzo [raw WAR].
-
Riferimenti
- Capire
l'ambiente JAX-RPC SI, Parte 1
- Java APIs for
XML-based RPC
- Java Web
Services Developer Pack
- Java
WSDP Tutorial
- Building
Web Services with JAX-RPC
- JAX-RPC
Tools
- SOAP
Message Handlers Example
- Dynamic
Proxy Example
- DII
Example
Note sull'autore
Arun Gupta è un Ingegnere del team di sviluppo delle API per
l' XML-based RPC (JAX-RPC) presso la Sun Microsystems. Ha maturato
oltre sette
anni di esperienza nell'industria del software, lavorando su svariate
tecnologie inerenti calcolo distribuito, Java, C++, Web. Attualmente fa
parte del team di sviluppo JAX-RPC e rappresenta la Sun all'interno del
WS-I Sample Application Working Group. Ha fatto parte del gruppo
JAX-RPC sin dalle sue origini ed ha apportato un contributo
significativo all'evoluzione della tecnologia.
Il team di sviluppo di JAX-RPC della Sun Microsystems ha fornito un
inestimabile contributo a questo white paper.
Per commenti scrivere a: users@jax-rpc.dev.java.net.
Note sul traduttore
Alessio Cervellin è nato nel 1977. E' Ingegnere diplomato
presso l' Università di Roma "La Sapienza" in Informatica ed
Automatica, e dal 2000 lavora come professionista nel settore dell'
Information Technology, specializzandosi sulla piattaforma Java e le
tecnologie
Web.
Per contatti:
a.cervellin@acm.org