Esercitazione JAX-WS Message Level


1.?Approccio orientato ai messaggi

Nelle ultime esercitazioni abbiamo visto come implementare un web service sia con un approccio Bottom Up, annotando le classi java e lasciando al framework il compito di generare il WSDL del servizio, sia Top Down, generando il codice partendo dal WSDL e implementandone la logica applicativa.

Se vogliamo invece operare a livello di messaggio, quindi lavorando direttamente sull'XML, avremo bisogno di un altro tipo di implementazione. Vediamo come reimplementare il servizio di Magazzino visto nell'ultima esercitazione con questo approccio.

Il nostro servizio deve implementare l'interfaccia javax.xml.ws.Provider ed essere annotato come @WebServiceProvider:

import javax.xml.ws.Provider;
import javax.xml.ws.WebServiceProvider;
import javax.xml.soap.SOAPMessage;

@WebServiceProvider()
@ServiceMode(value=Service.Mode.PAYLOAD)
public class Magazzino implements Provider<Source> {

   public Source invoke(Source source) {
    	// logica del servizio
    }
}
[Nota] Nota

l'interfaccia Provider è specializzata con più classi di implementazione. Nel nostro esempio utilizziamo la classe Source, ma sono disponibili anche le classi SOAPMessage (solo per i messaggi SOAP) e DataSource (da usare in presenza di allegati).

[Nota] Nota

@ServiceMode indica a quale livello di dettaglio si vuole operare. Nel caso MESSAGE viene passato il messaggio completo, mentre nel caso PAYLOAD viene passato solo il contenuto del Body.

I livelli di dettaglio possibili dipendono dal tipo di Provider scelto. Per una mappa delle combinazioni possibili consultare questa pagina

In questa modalità abbiamo una sola operazione che gestisce qualsiasi chiamata al servizio. Vediamo una possibile implementazione che replica il comportamento del servizio Magazzino.

import javax.annotation.Resource;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;

@WebServiceProvider()
@ServiceMode(value=Service.Mode.PAYLOAD)
public class Magazzino implements Provider<Source> {

	// Injection del contesto del messaggio. 
	// Serve per recuperare i dati del servizio e del messaggio
	@Resource WebServiceContext wsCtx;
	
	public Source invoke(Source source) {
		try {
			// Costruisco il DOM del contenuto del BODY (PAYLOAD)
			DOMResult dom = new DOMResult();
			Transformer trans = TransformerFactory.newInstance().newTransformer();
			trans.transform(source, dom);

			// Recupero l'operazione richiesta dall'header HTTP SOAPAction
			Map<?,?> httpHeadersMap = (Map<?,?>) wsCtx.getMessageContext().get(MessageContext.HTTP_REQUEST_HEADERS); 
			String operation = (String) ((List<?>) httpHeadersMap.get("SOAPAction")).get(0);

			if(operation.equals("mostraOrdini")) {
				// Operazione mostraOrdini.

                                // Recupero il valore del codice fiscale dal dom dell'xml

                                // E' possibile farlo usando le tecniche di navigazione del DOM gi? viste
                                // Qui lo facciamo utilizzando il linguaggio XPath
				XPath xPath = XPathFactory.newInstance().newXPath();
				String codFisc = xPath.compile("//*[contains(local-name(), 'codfisc')]").evaluate(dom.getNode());
				
				System.out.println("Richiesta lista ordini per il codice fiscale " + codFisc);
				
				// Costruisco il messaggio di risposta.
				return new StreamSource(new ByteArrayInputStream("<listaOrdini xmlns=\"http://www.negozio.org/\"><idOrdine>1000</idOrdine></listaOrdini>".getBytes()));
				
			} else {
				throw new RuntimeException("Operazione sconosciuta");
			}
		}
		catch (TransformerException e) {
			throw new RuntimeException("Errore nella trasformazione dell'XML in DOM", e);
		} catch (XPathExpressionException e) {
			throw new RuntimeException("Errore nella ricerca XPath", e);
		}
	}
}
[Nota] Nota

La procedura di deploy di un servizio implementato a livello di messaggio è identica a quella vista per il servizio implementato a livello di operazione.

Possiamo testare il servizio utilizzando il solito curl con il messaggio request.soap.v3.xml dell'esercitazione precedente:

curl -v -H "Content-Type:text/xml" -H  "SOAPAction:mostraOrdini" -X POST -v -T request.soap.v3.xml "http://localhost:8080/Magazzino/GestioneOrdini"

Se analizziamo il WSDL esposto da questo Web Service (http://localhost:8080/Magazzino/GestioneOrdini?wsdl) vediamo che si tratta di un WSDL generico che non pone vincoli sulla struttura del messaggio. Qualsiasi messaggio SOAP è accettato. Se vogliamo che venga esposto un WSDL con valori non di default oppure un wsdl totalmente diverso, basta impostare uno o più parametri dell'annotazione WebServiceProvider:

@WebServiceProvider(
		portName="...",
		serviceName="...",
		targetNamespace="...",
		wsdlLocation="...")
Footer BGFooter BG
Tito Flagella - © 2007-2015