Introduzione

Spring-WS è un framework per lo sviluppo di Web Services che segue un approccio "contract first". In questa serie di articoli vedremo come realizzare un Web Service seguendo questa metodologia, che prevede la definizione anticipata del contratto XML prima dell'implementazione. Prima di lanciarci nello sviluppo pratico, è necessario fare alcune considerazioni metodologiche sulle strategie da seguire.

L'approccio "contract first" comporta la definizione dello schema XSD prima di scrivere il codice Java. Questo approccio può sembrare meno intuitivo rispetto al classico "code first", ma offre numerosi vantaggi in termini di interoperabilità, performance e riusabilità. Vediamo un esempio di come gli ambienti di sviluppo possano violare le specifiche quando si parte dal codice:

map.put("04016", "Sabaudia");
map.put("04017", "San Felice Circeo");
map.put("04019", "Terracina");
map.put("04020", "Ventotene");
map.put("04027", "Ponza");
map.put("04029", "Sperlonga");
/* ...............*/

return map;
}

<< Fig. 1: Eclipse, violazione della specifica JAX-RPC 1.1. >>

<< Fig. 1: Eclipse, violazione della specifica JAX-RPC 1.1. >>

In questa maniera gli ambienti di sviluppo preservano il rischio di violare il principio di interoperabilità quando si cerca di creare Web Services a partire da tipi che non trovino corrispondenza alla specifica JAX-RPC 1.1.

Performance

La serializzazione automatica in XML di una classe, per esempio implementata in Java o in uno dei linguaggi della piattaforma .NET, potrebbe nascondere l'effettiva dimensione del payload trasferito sul protocollo SOAP. A questo si aggiunge l'overhead computazionale di conversione da e verso l'XML che diventa tanto più significativo quanto maggiore è la complessità degli oggetti. Approcciando la progettazione con la definizione anticipata dell'XML il progettista ha sempre il controllo delle dimensioni del payload che viene trasferito, e della complessità computazionale della trasformazione da e verso l'XML.

Riusabilità

La definizione di una "type inventory" a cui attenersi nella definizione dei contratti pubblicati, introduce un indiscutibile grado di robustezza nella progettazione dei servizi. I progettisti, a partire dai tipi primitivi della specifica JAX-RPC 1.1, definiranno gli schema XML dei tipi di base del business che vogliono modellare. In questo modo all'interno del nostro service bus ci sarà un'unica definizione dei tipi di base che potranno essere riutilizzati, attraverso opportune direttive di import, nei diversi scenari applicativi. Più è ampio il progetto di sviluppo tanto più importante diventa questo aspetto di centralizzazione: la proliferazione di definizioni diverse dello stesso tipo potrebbe dare origine a disallineamenti noiosi da gestire. Riportiamo di seguito un esempio che definisce all'interno del service bus il codice di avviamento postale.

<simpleType name="CodiceAvviamentoPostale">
<restriction base="numeric">
<pattern value="[0-9][0-9][0-9][0-9][0-9]"/>
</restriction>
</simpleType>

<< Listato 2: Definizione di un tipo complesso all'interno del service bus. >>

<< Listato 2: Definizione di un tipo complesso all'interno del service bus. >>

Gestione delle versioni

Per definizione la validità di un contratto è strettamente legata al fatto di restare nel tempo il più possibile costante. Sappiamo bene che questo accade raramente e che ci si trovi a dover modificare l'interfaccia del servizio con conseguente codifica di una nuova implementazione. L'approccio "contract first", poiche' definisce una scarso accoppiamento tra il contratto e la sua implementazione, ci permette di implementare una nuova versione del servizio nella stessa classe di implementazione di quello originario. Si può quindi utilizzare una trasformazione XSLT per la conversione del vecchio messaggio nel nuovo. Saremo così in grado di pubblicare ambedue le versioni del contratto dismettendo quello obsoleto nel momento in cui tutti i consumatori avranno aderito alla nuova interfaccia.

Implementazione con Spring-WS

Illustriamo l'implementazione di un Web Service seguendo un approccio "contract first" attraverso il framework di Spring-WS. Questo framework media la complessità di questo approccio in quanto lo sviluppatore non dovrà codificare il WSDL, che sarà generato dal framework a partire dall'XML della response e della request definito per il contrato del servizio. Il WSDL generato sarà in stile document e uso literal. Stiamo parlando dello stile di binding SOAP, che in un Web Service può essere document o RPC. In corrispondenza di questo valore il client capisce che dovrà far uso di schema XML piuttosto che utilizzare convenzioni di chiamate di procedure remote. Vedremo quindi come la realizzazione del Web Service si articola nella definizione dell'XML del contratto, della configurazione dei file di configurazione di Spring-WS e nella codifica delle classi di implementazione dell'endpoint e del servizio.

Modello UML e interfaccia del servizio

La prima cosa da fare è definire l'XML che rappresenta la request, la response e l'interfaccia del servizio web che vogliamo realizzare. La realizzazione che presentiamo è quella di un banale servizio di acquisto dove la request è costituita da tre tipi complessi, che rappresentano le informazioni circa l'acquisto, l'indirizzo di spedizione della merce e le informazioni di pagamento attraverso carta di credito. La response del servizio è definita da un tipo complesso che rappresenta i dati relativi all'acquisto. Riportiamo di seguito i corrispondenti class diagram e schema XSD.

<< Fig. 2: Class diagram della request. >>

<< Fig. 2: Class diagram della request. >>

<< Fig. 3: Class diagram della response. >>

<< Fig. 3: Class diagram della response. >>

A partire dai class diagram della request e response codifichiamo lo schema XSD che rappresenta il contratto del servizio:

<?xml version="1.0" encoding="UTF-8"?>
<schema
xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.luigibennardis.it/spring-ws/Ecomm/Acquisto/types"
xmlns:tns="http://www.luigibennardis.it/spring-ws/Ecomm/Acquisto/types"
elementFormDefault="qualified">

<!-- REQUEST  -->
<element name="AcquistoOnLineRequest">
<complexType>
<all>
<element name="Acquisto" type="tns:AcquistoType"/>
<element name="Indirizzo" type="tns:IndirizzoType"/>
<element name="Pagamento" type="tns:PagamentoType"/>
</all>
</complexType>
</element>

<complexType name="AcquistoType">
<sequence>
<element name="DataAcquisto" type="date"/>
<element name="CodiceProdotto" type="string"/>
<element name="Quantita" type="integer"/>
<element name="PrezzoUnitario" type="double"/>
<element name="PrezzoTotale" type="double"/>
</sequence>
</complexType>
<complexType name="IndirizzoType">
<sequence>
<element name="Nome" type="string"/>
<element name="Cognome" type="string"/>
<element name="Indirizzo" type="string"/>
<element name="Cap" type="string"/>
<element name="Citta" type="string"/>
</sequence>
</complexType>
<complexType name="PagamentoType">
<sequence>
<element name="TipoCarta" type="string"/>
<element name="NumeroCarta" type="string"/>
<element name="Intestazione" type="string"/>
<element name="Scadenza" type="string"/>
</sequence>
</complexType>

<!-- RESPONSE  -->
<element name="AcquistoOnLineResponse">
<complexType>
<all>
<element name="AcquistoRisposta" type="tns:AcquistoRispostaType"/>
</all>
</complexType>
</element>

<complexType name="AcquistoRispostaType">
<sequence>
<element name="DataAcquisto" type="date"/>
<element name="CodiceAcquisto" type="string"/>
<element name="CodiceAutorizzazioneCarta" type="string"/>
</sequence>
</complexType>
</schema>

Avremmo potuto utilizzare la direttiva di import per ciascun tipo definito dalla request e dalla response:

<xsd:import namespace="http://www.luigibennardis.it/spring-ws/Ecomm " schemaLocation=<"it.luigibennardis.spring-ws.datatypes.acquistotype.xsd"/>

<xsd:import namespace="http://www.luigibennardis.it/spring-ws/Ecomm " schemaLocation=<"it.luigibennardis.spring-ws.datatypes.indirizzotype.xsd"/>

In questo modo non solo si ottiene una maggiore leggibilità, ma sarà anche possibile definire tutti i contratti dei servizi che si vogliono pubblicare a partire dalla definizione centralizzata di uno schema inventory. Si evitano così dannose duplicazioni: tutti i progettisti referenzieranno sempre questo inventory da cui creeranno i contratti dei servizi che vogliono realizzare.

<< Fig. 4: Schema del servizio. >>

<< Fig. 4: Schema del servizio. >>

Definiamo quindi l'interfaccia e la classe di implementazione del servizio riportata nel seguente diagramma UML.

<< Fig. 5: Class diagram dell'implementazione del servizio. >>

<< Fig. 5: Class diagram dell'implementazione del servizio. >>

Conclusioni

Per oggi ci fermiamo qui, anche se resta ancora molto da fare per portare a termine lo sviluppo del nostro web service. Abbiamo comunque dato una panoramica su alcuni aspetti importanti che sono alla base di certe scelte e abbiamo cominciato a gettare le fondamenta del nostro web service che continueremo a sviluppare nel prossimo articolo. Ci interessa soprattutto aver messo in luce quanto sia importante l'approccio "document-first". Questo framework , preservando il principio di interoperabilità, agevola l'applicazione di questo approccio in quanto risparmia allo sviluppatore la codifica del WSDL. Allo stesso tempo Spring-WS realizza un modello di sviluppo che disaccoppia, in stile MVC, l'implementazione dell'endpoint da quella della logica funzionale, che lo sviluppatore dovrà in ogni caso codificare.

Riferimenti

Arjen Poutsma, Rick Evans, Tareq Abed Rabbo, "Spring Web Services. Reference Documentation"

Blog "Shared Experience" http://demirm.blogspot.com/

Best practices for interoperable web service applications www.frotscher.com/download/JUG-ContractFirst.pdf

Apache Muse - Conflicting Version of WSDL4J in WebSphere Deployments http://ws.apache.org/muse/docs/2.2.0/manual/troubleshooting/wsdl4j-version-on-websphere.html

Web Services Description Language (WSDL 1.1) http://www.w3.org/TR/wsdl

Web Services Description Language (WSDL 2.0) http://www.w3.org/TR/2007/REC-wsdl20-primer-20070626/

Metro Web Services for the Java Platform http://java.sun.com/webservices/index.jsp

The Web Services Interoperability Organization http://www.ws-i.org/

Spring Framework http://static.springsource.org/spring-ws/sites/1.5/reference/html/tutorial.html

IBM sulla scelta del WSDL http://www.ibm.com/developerworks/webservices/library/ws-whichwsdl/

IBM sui web service "document style" http://www.ibm.com/developerworks/webservices/library/ws-docstyle.html

IBM sul mapping da WSDL a SOAP http://www.ibm.com/developerworks/webservices/library/ws-tip-namespace.html