Search This Blog

Saturday 3 May 2014

Interceptors in CXF

In an earlier post I used JAX-WS Handlers to modify the soap body and soap headers. The same can be achieved by directly writing CXF Interceptors . Consider our random web service operation that returns a random result. I decided to add an interceptor to modify the SOAP body
public class BodyInterceptor extends AbstractPhaseInterceptor<Message> {

  public BodyInterceptor() {
    super(Phase.PRE_INVOKE);
    System.out.println("Creating Instance");
  }

  @Override
  public void handleMessage(final Message message) throws Fault {
    System.out.println("Formats message is available in : " + message.getContentFormats());
    final List contents = message.getContent(List.class);
    for (final Object object : contents) {
      if (object instanceof GetRandomRequest) {
        final GetRandomRequest request = (GetRandomRequest) object;
        System.out.println("Request value is " + request.getName());
        request.setName("INVALID");
      } else {
        System.out.println("Object found is " + object);
      }

    }

  }

}
My interceptor does not directly extend the Interceptor interface:
public interface Interceptor<T extends Message> {

  public abstract void handleMessage(T paramT) throws Fault;
  public abstract void handleFault(T paramT);
}
Instead my class extends the AbstractPhaseInterceptor class. As per the documentation:
Provides a starting point implementation for a interceptors that participate in phased message processing.
Developers should extend from this class when implementing custom interceptors. Developers need to
provide an implementation for handleMessage() and can override the handleFault() implementation.
They should not override the other methods.
The AbstractPhaseInterceptor implements the PhaseInterceptor interface. This interface introduces the methods related to Phase. The PhaseInterceptor extends the Interceptor interface. Accordingly I have overriden the handleMessage method. ithin the message we have got access to the Request which is modified. The configuration is as below:
<jaxws:endpoint id="randomWs"
      implementor="com.ws.service.samplews_ns.SampleServiceOperationsPortTypeImpl"
      wsdlLocation="random.wsdl" address="randomService">
      <jaxws:inInterceptors>
         <bean class="com.ws.service.samplews_ns.interceptor.BodyInterceptor" />
      </jaxws:inInterceptors>
   </jaxws:endpoint>
On testing the web service the logs indicate the below:
211116 [http-bio-8880-exec-3] DEBUG org.apache.cxf.phase.PhaseInterceptorChain  
- Chain org.apache.cxf.phase.PhaseInterceptorChain@c6552a was modified. Current flow:
  receive [PolicyInInterceptor, AttachmentInInterceptor]
  pre-stream [CertConstraintsInterceptor]
  post-stream [StaxInInterceptor]
  read [WSDLGetInterceptor, ReadHeadersInterceptor, SoapActionInInterceptor, StartBodyInterceptor]
  pre-protocol [MEXInInterceptor, MustUnderstandInterceptor]
  post-protocol [CheckFaultInterceptor, JAXBAttachmentSchemaValidationHack]
  unmarshal [DocLiteralInInterceptor, SoapHeaderInterceptor]
  pre-logical [OneWayProcessorInterceptor]
  post-logical [WrapperClassInInterceptor]
  pre-invoke [SwAInInterceptor, HolderInInterceptor, BodyInterceptor]
  invoke [ServiceInvokerInterceptor]
  post-invoke [OutgoingChainInterceptor, StaxInEndingInterceptor]
...
 Formats message is available in : [interface java.util.List, interface org.w3c.dom.Node, class java.io.InputStream,
 class org.apache.cxf.io.DelegatingInputStream, interface javax.xml.stream.XMLStreamReader]
...
 Request value is coolio
As seen here the request content is available in the interceptor in a variety of message formats. From the Message documentation:
The content is available as a result type if the message is outbound. The content
is available as a source type if message is inbound. If the content is not available as 
the specified type null is returned.
Thus we have specified the list type allowing us to get access to the input as a List of objects.
This interceptor ran for the inbound message. We can also execute interceptors on the outbound chain:
public class BodyResponseInterceptor extends AbstractPhaseInterceptor<Message> {

  public BodyResponseInterceptor() {
    super(Phase.PREPARE_SEND);
    System.out.println("Creating Instance");
  }

  @Override
  public void handleMessage(final Message message) throws Fault {
    System.out.println("Formats message is available in : " + message.getContentFormats());
    final List contents = message.getContent(List.class);
    for (final Object object : contents) {
      if (object instanceof GetRandomResponse) {
        final GetRandomResponse response = (GetRandomResponse) object;
        System.out.println("Response value is " + response.getValue());
        response.setValue(-1);
      } else {
        System.out.println("Object found is " + object);
      }

    }

  }

}
The configuration :
<jaxws:endpoint id="randomWs"
      implementor="com.ws.service.samplews_ns.SampleServiceOperationsPortTypeImpl"
      wsdlLocation="random.wsdl" address="randomService">
      <jaxws:outInterceptors>
         <bean class="com.ws.service.samplews_ns.interceptor.BodyResponseInterceptor" />
      </jaxws:outInterceptors>
   </jaxws:endpoint>
On running the change is visible in the logs:
36092 [http-bio-8880-exec-5] DEBUG org.apache.cxf.phase.PhaseInterceptorChain  
- Chain org.apache.cxf.phase.PhaseInterceptorChain@4fd9908d was modified. Current flow:
  setup [PolicyOutInterceptor]
  pre-logical [HolderOutInterceptor, SwAOutInterceptor, WrapperClassOutInterceptor, SoapHeaderOutFilterInterceptor]
  post-logical [SoapPreProtocolOutInterceptor]
  prepare-send [MessageSenderInterceptor, BodyResponseInterceptor]
  pre-stream [AttachmentOutInterceptor, StaxOutInterceptor]
  write [SoapOutInterceptor]
  marshal [BareOutInterceptor]
  prepare-send-ending [MessageSenderEndingInterceptor]

36092 [http-bio-8880-exec-5] DEBUG org.apache.cxf.phase.PhaseInterceptorChain  
- Invoking handleMessage on interceptor com.ws.service.samplews_ns.interceptor.BodyResponseInterceptor@5a978023
Formats message is available in : [interface java.util.List, class java.io.OutputStream]
Response value is 89

7 comments:

  1. I've tried every single phase with inputstream and xmlstream reader and got nothing changed in the inbound SOAP message even though the interceptor has been invoked.
    This tiny code works perfectly. Thank you so much and I wish you all the best to get a great job.

    thanks thanks

    Sarra

    ReplyDelete
  2. I revamped a SOAP WS with new one. The new service operation takes entirely new request xml. But I am supposed to support the older request xml with this. So, I tried to transform the older request xml to a new one (even the root element changed). But getting error as older request xml is not recognised by wsdl. What is the best approach for this scenario?

    ReplyDelete
    Replies
    1. Ideally you should keep the existing service as is and introduce a new V2 service. That ways evrrything remains clean, you can support existing clients and upgrade the new API of V2 freely

      Delete
  3. Hi!
    Can you guide me with implementing the MustUnderstandInterceptor?

    ReplyDelete
  4. Extremely taken joy in while looking at it. Many thanks for exchanging this very useful facts.
    Custom Boxes Wholesale

    ReplyDelete