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.

  1. Compilare l'interfaccia e le classi di implementazione dell'endpoint del servizio.
  2. 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.
  3. 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:
     

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

  1. 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.
  2. 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.
  3. 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.

  1. 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
  2. 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.
  3. 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. 
  4. 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:


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

  1. Capire l'ambiente JAX-RPC SI, Parte 1
  2. Java APIs for XML-based RPC
  3. Java Web Services Developer Pack
  4. Java WSDP Tutorial
  5. Building Web Services with JAX-RPC
  6. JAX-RPC Tools 
  7. SOAP Message Handlers Example
  8. Dynamic Proxy Example
  9. 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