Molte delle funzionalità JAX-WS che JBossWS fornisce in WildFly sono ottenute attraverso la corretta integrazione dello stack JBoss Web Services con la maggior parte dei moduli di progetto Apache CXF (nel seguito JBossWS-CXF). Tale integrazione è principalmente finalizzata a:
- Consentire l’utilizzo di API standard (incluso JAX-WS) su WildFly.
- Includere le funzionalità avanzate di Apache CXF (incluso WS-*).
In questo articolo vediamo come, attraverso l’integrazione JBossWS-CXF, sia possibile intercettare le chiamate web-service, in ingresso o in uscita, al fine di leggere o modificare le informazioni scambiate nella request o nella response del servizio.
Il Progetto
Riprendiamo il progetto jaxws-webapp che abbiamo utilizzato nell’articolo Primi Passi con JAX-RS. Estendiamo l’esempio base introducendo le classi Request
e Header
così definite:
|
|
L’idea è quella di avere una request generica per tutti i servizi, che include sempre un bean Header
contenente informazioni comuni a tutte le chiamate. Nel nostro esempio abbiamo inserito la proprietà sender
che identifica il chiamante ed intendiamo recuperare l’header attraverso un interceptor per renderlo disponibile a tutta l’applicazione attraverso un oggetto ThreadLocal.
L’Interceptor
CXF consente la dichiarazione di un interceptor in diversi modi. Manteniamo però la natura annotation based del progetto e quindi utilizziamo l’annotazione @org.apache.cxf.interceptor.InInterceptor
, che consente di intercettare la request in ingresso. Analogamente è possibile intercettare la response del servizi utilizzando l’annotazione @org.apache.cxf.interceptor.OutInterceptor
. Nella dichiarazione dell’end-point del servizio HelloWord
, implementato da HelloWordImpl
, introduciamo quindi l’annotazione richiesta per l’interceptor ottenendo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@WebService(endpointInterface = "it.javaboss.jaxws.HelloWorld", targetNamespace = "http://hello.world.ns/", name = "HelloWorld", serviceName = "HelloWorldService", portName = "HelloWorldPort") @SOAPBinding(style = Style.DOCUMENT, use = Use.LITERAL) @InInterceptors( interceptors = { "it.javaboss.jaxws.HeaderInterceptor" } ) public class HelloWorldImpl implements HelloWorld { public String greetings(Request request) { System.out.println(request.getPerson()); return "Greetings " + request.getPerson().getName() + " " + request.getPerson().getSurname(); } } |
In questo modo tutte le chiamate verso i servizi esposti dalla classe, ed in particolare verso il metodo geetings()
, attiveranno l’interceptor HeaderInterceptor
. Si noti che è anche possibile definire più interceptor attivati nella stessa invocazione del servizio.
CXF definisce una gerarchia di interceptor molto complessa a partire dall’interfaccia org.apache.cxf.interceptor.Interceptor
. Nel nostro esempio utilizzeremo un PhaseInterceptor
, che consente di controllare la fase di elaborazione del messaggio in cui l’interceptor è invocato, ed in particolare estenderemo la classe astratta AbstractPhaseInterceptor
che ci consente di concentrare i nostri sforzi nel solo metodo di elaborazione del messaggio. Il bean HeaderInterceptor
ha quindi il seguente aspetto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class HeaderInterceptor extends AbstractPhaseInterceptor<Message> { public HeaderInterceptor() { super(Phase.PRE_INVOKE); } public void handleMessage(final Message message) throws Fault { final List<?> contents = message.getContent(List.class); for (final Object object : contents) { if (object instanceof Request) { final Request request = (Request) object; RequestContextHolder.setRequestHeader( request.getHeader() ); } } } } } |
Nella dichiarazione di un PhaseInterceptor
è necessario definire la fase di elaborazione in cui si intende intervenire. Questo è fatto nel costruttore del bean che implementa l’interceptor. Per un elenco delle possibili fasi, sia in ingresso che in uscita al servizio, si rimanda alla documentazione ufficiale raggiungibile al link Interceptors and Phases.
Il messaggio è invece elaborato dal metodo handleMessage()
, dichiarato nell’interfaccia Interceptor
e che deve essere sovrascritto dal bean. Nel nostro esempio il contenuto del messaggio viene letto ciclicamente fino ad ottenere un oggetto di tipo Request,
dal quale recuperiamo la proprietà header
che andiamo ad inserire in un oggetto ThreadLocal
, gestito dalla classe RequestContextHolder
.
1 2 3 4 5 6 7 8 9 10 11 |
public class RequestContextHolder { private static final ThreadLocal<Header> requestHeaderHolder = new ThreadLocal<Header>(); public static void setRequestHeader( Header header ) { requestHeaderHolder.set( header ); } public static Header getCurrentRequestHeader() { return requestHeaderHolder.get(); } } |
Codice Sorgente
Il codice sorgente dell’esempio è scaricabile qui jaxws-webapp-interceptor.
Errata Corrige
Ne progetto scaricabile manca una componente fondamentale affinché l’interceptor si attivi, ovvero la configurazione del MANIFEST.MF
da inserire all’interno della cartella resources/META-INF
e deve avere le dipendenze necessarie:
1 2 3 |
Manifest-Version: 1.0 Dependencies: org.apache.cxf, org.apache.ws.security, org.apache.cxf.impl Class-Path: |
pom.xml
deve essere configurato il plug-in per la generazione del war nel seguente modo:
1 2 3 4 5 6 7 8 9 |
<plugin> <artifactId>maven-war-plugin</artifactId> ... <configuration> <archive> <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile> </archive> </configuration> </plugin> |