In questa lezione introdurremo il WSDL alla base degli esempi che seguiranno nei prossimi capitoli, e i progetti Eclipse, allegati alla guida, lato client e server per esporre e invocare il servizio descritto nel WSDL. Si descriveranno inoltre nel dettaglio i progetti di base e relativo codice, privi di riferimenti alla sicurezza, essi faranno da base per gli esempi dei successivi capitoli. Il progetto di base sarà organizzato in modo da facilitare le operazioni di compilazione del WSDL.
Segue quindi la descrizione dei progetti allegati alla guida.
PresentazioneServer
: progetto contenente il WSDL di base e la relativa implementazione;PresentazioneClient
: contenente sia invocazioni basiche del servizio che una classe che fa riferimento a keystores per eseguire accessi tramite HTTPS;PresentazioneServerWSSecurity
: contenente il WSDL di base e diverse implementazioni di meccanismi di sicurezza che implementano lo standard WS-Security;PresentazioneClientCXF
: contenente invocazioni del servizio che utilizzano lo stack CXF per implementare lo standard WS-Security;PresentazioneServerWSSecurityPolicy
: contenente il WSDL modificato per esporre politiche di sicurezza secondo lo standard WS-SecurityPolicy e un’implementazione delle policies descritte.
Da osservare che il progetto PresentazioneClientCXF
fa riferimento a librerie dello stack CXF che non sono contenute nei progetti allegati per motivi di dimensione dei file. E’ possibile reperirle nella cartella JBoss[vers]/client
, eventualmente copiandole nella cartella predisposta lib
dei progetti e poi importarle nel classpath
.
WSDL e XSD
Segue il sorgente di documento.wsdl
<?xml version="1.0" encoding="ISO-8859-1" ?>
<definitions name="TestSicurezza1"
targetNamespace=http://www.prova.documento.test/
xmlns:tns=http://www.prova.documento.test/
xmlns:schema=http://www.prova.schema.test/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<xsd:schema targetNamespace="http://www.prova.documento.test/">
<xsd:import namespace="http://www.prova.schema.test/" schemaLocation="schema.xsd" />
</xsd:schema>
</types>
<message name="PresentazioneReq">
<part name="parameter" element="schema:Presentazione" />
</message>
<message name="PresentazioneResp">
<part name="parameter" element="schema:PresentazioneRisposta" />
</message>
<portType name="ServizioPresentazioneIF">
<operation name="Presentazione">
<input message="tns:PresentazioneReq"/>
<output message="tns:PresentazioneResp"/>
</operation>
</portType>
<binding name="ServizioPresentazioneImplPortBinding" type="tns:ServizioPresentazioneIF">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="Presentazione">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="PresentazioneService">
<port name="ServizioPresentazioneImpl" binding="tns:ServizioPresentazioneImplPortBinding">
<soap:address location="http://localhost:8080/Presentazione/PresentazioneService/ServizioPresentazioneIF"/>
</port>
</service>
</definitions>
Mentre il codice seguente è quello relativo a schema.xsd
:
targetNamespace="http://www.prova.schema.test/"
>
<xs:import namespace="http://www.w3.org/2001/XMLSchema" />
<xs:element name="Presentazione">
<xs:complexType>
<xs:sequence>
<xs:element name="Nome" type="xs:string" />
<xs:element name="Cognome" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="PresentazioneRisposta">
<xs:complexType>
<xs:sequence>
<xs:element name="Risposta" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Il WSDL e lo schema sono praticamente gli stessi usati in WSDL: stili e codifiche. Il WSDL si limita ad esporre un’operazione con in ingresso una coppia di attributi, due stringhe "nome – cognome", e in uscita un attributo anch’esso di tipo stringa. La compilazione la possiamo demandare ad uno script che inseriremo direttamente nel progetto Eclipse in un percorso apposito. L’immagine seguente mostra uno spaccato dei progetti.
Da notare che nei progetti PresentazioneServer
e PresentazioneClient
sono presenti altri files, packages e classi che verranno descritti successivamente, non essendo parte necessaria per l’esecuzione degli esempi di base.
Server, implementazione e struttura del progetto
Lato server si è utilizzato un progetto Eclipse del tipo Dynamic Web Project. Come al solito, poniamo XSD e WSDL nella cartella WEB-INF
, mentre nella cartella META-INF
possiamo inserire il solo XSD. Alla struttura predeterminata si è aggiunta una cartella, support
, con all'interno le cartelle build
e generated
. In build
è presente uno script, un .bat
per Windows, il cui compito è compilare il WSDL presente nel WEB-INF
e posizionare gli stubs generati in generated
:
@cd /D %~dp0
@wsimport -d ../generated -Xnocompile ../../WebContent/WEB-INF/wsdl/documento.wsdl
La prima riga serve a far sì che lo script venga lanciato direttamente dall’IDE, acquisendo il path nel quale è posizionato. Nella secondo troviamo l’invocazione del tool wsimport
, con le richieste di posizionare i sorgenti nella cartella generated
e di non compilarli. Per eseguire lo script direttamente da Eclipse tasto destro sul file e poi "Open With > Default Editor".
Per il progetto base stiamo utilizzando il tool wsimport
con il quale si generano gli stubs relativi allo stack JAX-WS. Eseguito lo script, nella cartella generated
(dopo il refresh) sarà possibile trovare la cartella test
con l’organizzazione in package degli stubs. E’ possibile copiare quest'ultima così com’è in src
. A questo punto manca solo l’implementazione da posizionare nel package prova.server
. Di seguito la classe.
package prova.server;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import org.jboss.wsf.spi.annotation.WebContext;
import test.documento.prova.ServizioPresentazioneIF;
@WebService(targetNamespace="http://www.prova.documento.test/", serviceName="PresentazioneService",
wsdlLocation="WEB-INF/wsdl/documento.wsdl",
portName="ServizioPresentazioneImpl",
name="ServizioPresentazioneIF")
@WebContext(contextRoot="/Presentazione")
@Remote(ServizioPresentazioneIF.class)
@Stateless
@SOAPBinding
(
style = SOAPBinding.Style.DOCUMENT,
use = SOAPBinding.Use.LITERAL
)
public class ServizioPresentazione implements ServizioPresentazioneIF{
@WebMethod
public String presentazione(String nome, String cognome) {
System.out.println("Web service, arrivata presentazione "+
"da parte di: "+nome+" "+cognome+"\n\n");
String risposta = "Ciao "+nome+"!";
return risposta;
}
}
Dall’annotazione @Stateless
posiamo vedere che andremo a deployare il servizio come EJB nell’Application Server. Con i JBossTools è possibile eseguire questo passaggio tramite comode interfacce grafiche. Per questi test, sarà sufficiente un server di tipo default.
Client, implementazione e struttura del progetto
Lato client, è sufficiente un normale progetto Java. Possiamo crearvi una cartella META-INF
per ospitare WSDL e schemi, copiare e incollare direttamente nella cartella src
gli stub creati in precedenza nel progetto lato server. Resta da implementare l’effettiva invocazione del servizio. A tal proposito, creiamo due classi nel package wsClient
.
Classe ClientMETAInf.java
:
package wsClient;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import test.documento.prova.PresentazioneService;
import test.documento.prova.ServizioPresentazioneIF;
public class ClientMETAInf {
private ServizioPresentazioneIF port;
public ClientMETAInf(String urlPath){
if(urlPath==null) urlPath = "META-INF/wsdl/documento.wsdl";
try {
URL urlLocation = ClientMETAInf.class.getClassLoader().getResource(urlPath).toURI().toURL();
QName serviceName = new QName("http://www.prova.documento.test/", "PresentazioneService");
PresentazioneService srv = new PresentazioneService(urlLocation, serviceName);
port = srv.getPort(ServizioPresentazioneIF.class);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
};
}
public ServizioPresentazioneIF getServicePort(String prothostport){
BindingProvider bp = (BindingProvider)port;
try {
changeServiceEndPoint(prothostport, bp);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return port;
}
private void changeServiceEndPoint(String prothostport, BindingProvider intservice) throws MalformedURLException {
BindingProvider binding_bsn = (BindingProvider) intservice;
java.lang.String endpoint_url = (java.lang.String) binding_bsn.getRequestContext().get(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY);
URL url = new URL(endpoint_url);
String fddexturl = prothostport + url.getPath();
binding_bsn.getRequestContext().put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY, fddexturl);
}
}
Classe ServizioClient.java
:
package wsClient;
import test.documento.prova.ServizioPresentazioneIF;
public class ServizioClient {
public static void main(String[] args) {
String prot = "http";
String host = "localhost";
String port = "8080";
String protHostPort = prot+"://"+host+":"+port;
ClientMETAInf sensorClientSDM = new ClientMETAInf(null);
ServizioPresentazioneIF servicePort=sensorClientSDM.getServicePort(protHostPort);
String nome = "Micky";
String cognome = "Mouse";
System.out.println(nome+" "+cognome+" effettua la presentazione. ");
String risposta=servicePort.presentazione(nome, cognome);
System.out.println("Risposta ottenuta: "+risposta);
}
}
Osserviamo che la classe ClientMETAInf
mette a disposizione un metodo per restituire il porto del servizio dopo aver effettuato la modifica dell’end-point su cui eseguire l’effettiva invocazione del servizio, ciò anche all’esterno della macchina sulla quale viene operata l’invocazione. A tal fine, oltre alla possibilità di fornire il path al WSDL (viceversa viene cercato nella posizione di default, ossia quella relativa al momento in cui il WSDL è stato compilato), viene richiesta una stringa contenente la tripla di informazioni "protocollo-host-porto".
La classe ServizioClient
contiene il main
. Estremamente semplice, sarà la classe che utilizzeremo per eseguire l’invocazione del servizio. Individuati i parametri di connessione (nell’esempio la tripla "http-localhost-8080"), si utilizza la classe ClientMETAInf
per ottenere il porto tramite il quale eseguire le operazioni, ossia si effettua il binding al servizio. Segue l’invocazione con semplici parametri d’esempio ("Micky - Mouse") e la stampa a video della risposta ottenuta. Nella prossima lezione le prove d'esecuzione osservando con Wireshark cosa viaggia su rete.