I have been trying out some things in CXF (and also feeling pretty good about it :P ) The other day I needed to modify the message of more than one web service in a common manner. I achieved the same using a CXF interceptor. However another team member did the same task using Handlers. Interestingly Handler is more a part of the JAX-WS vocabulary than Interceptor. So I decided to look at this option too.
What exactly is a handler?
From jax-ws.java.net site
We need to bring this handler into play. That means configuring CXF to use this handler.
If we now start our server:
Now If I were to test the code:
What exactly is a handler?
From jax-ws.java.net site
Handlers are message interceptors that can be easily plugged in to the JAX-WS runtime to do additional processing of the inbound and outbound messages. Handlers are invoked with a message context that provides methods to access and modify inbound and outbound messages and to manage a set of properties.I decided to add one such Handler to the wsdl first example:
public class CrazyHandler implements LogicalHandler<LogicalMessageContext> { private static final Logger LOG = Logger.getLogger(CrazyHandler.class.getName()); @Override public boolean handleMessage(final LogicalMessageContext context) { LOG.info("call came handleMessage"); return true; } @Override public boolean handleFault(final LogicalMessageContext context) { LOG.info("call came handleFault"); return false; } @Override public void close(final MessageContext context) { LOG.info("call came close"); } }The class implements a Logical Handler. The javax.xml.ws.handler.LogicalHandler instances "are protocol-agnostic and cannot change any protocol-specific parts (like headers) of a message. Logical handlers act only on the payload of the message."
We need to bring this handler into play. That means configuring CXF to use this handler.
<jaxws:endpoint id="randomWs" implementor="com.ws.service.samplews_ns.SampleServiceOperationsPortTypeImpl" wsdlLocation="random.wsdl" address="randomService"> <jaxws:handlers> <bean class="com.ws.service.samplews_ns.handler.CrazyHandler"/> </jaxws:handlers> </jaxws:endpoint>As seen here we have associated a handler with our endpoint.
If we now start our server:
1431 [localhost-startStop-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'com.ws.service.samplews_ns.handler.CrazyHandler#17e7f88'Thus for our endpoint an inner bean instance was created of type CrazyHandler and associated with it.
Now If I were to test the code:
final String endpointAddress = "http://localhost:8080/Handler/services/randomService"; final JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); factory.setServiceClass(SampleServiceOperationsPortType.class);
//the SEI
factory.setAddress(endpointAddress); final SampleServiceOperationsPortType client = (SampleServiceOperationsPortType) factory.create(); final GetRandomRequest randomRequest = new GetRandomRequest(); randomRequest.setName("coolio"); final GetRandomResponse reply = client.random(randomRequest); System.out.println("Server said: " + reply.getValue());For this request, I decided to watch the behavior from the logs. Here are some observations:
- When CXF received the request, it created an interceptor chain to handle this request:
224440 [http-bio-8080-exec-3] DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Chain org.apache.cxf.phase.PhaseInterceptorChain@cce15ab was modified. Current flow: receive [PolicyInInterceptor, AttachmentInInterceptor] pre-stream [CertConstraintsInterceptor] post-stream [StaxInInterceptor] read [WSDLGetInterceptor, ReadHeadersInterceptor, SoapActionInInterceptor, StartBodyInterceptor] pre-protocol [MEXInInterceptor, MustUnderstandInterceptor] pre-protocol-frontend [SOAPHandlerInterceptor, LogicalHandlerInInterceptor] post-protocol [CheckFaultInterceptor, JAXBAttachmentSchemaValidationHack] unmarshal [DocLiteralInInterceptor, SoapHeaderInterceptor] pre-logical [OneWayProcessorInterceptor] post-logical [WrapperClassInInterceptor] pre-invoke [SwAInInterceptor, HolderInInterceptor] invoke [ServiceInvokerInterceptor] post-invoke [OutgoingChainInterceptor, StaxInEndingInterceptor]
-
As seen above, CXF actually used an interceptor to execute the handler. If we look closely, we can see that the PhaseInterceptorChain passes control to the LogicalHandlerInInterceptor in the pre-protocol-frontend phase. This interceptor than invokes all handlers for the inbound flow i.e. incoming request. accordingly our CrazyHandler is invoked and the message is logged.
... 224460 [http-bio-8080-exec-3] DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Invoking handleMessage on interceptor org.apache.cxf.jaxws.handler.logical.LogicalHandlerInInterceptor@43e73c9a 224464 [http-bio-8080-exec-3] DEBUG org.apache.cxf.jaxws.handler.HandlerChainInvoker - invoking handlers, direction: inbound 224470 [http-bio-8080-exec-3] DEBUG org.apache.cxf.jaxws.handler.HandlerChainInvoker - invoking handler of type com.ws.service.samplews_ns.handler.CrazyHandler 224470 [http-bio-8080-exec-3] INFO com.ws.service.samplews_ns.handler.CrazyHandler - call came handleMessage
-
In the invoke phase the ServiceInvokerInterceptor begins the call that eventually lands up at our endpoint:
224496 [http-bio-8080-exec-3] DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Invoking handleMessage on interceptor org.apache.cxf.interceptor.ServiceInvokerInterceptor@3d38d0ab Nov 27, 2013 11:16:35 AM com.ws.service.samplews_ns.SampleServiceOperationsPortTypeImpl random INFO: Executing operation random com.ws.service.xsd.random_schema.GetRandomRequest@bcf1913
-
After all the phases associated with the inbound request are processed, CXF creates a separate chain for the response processing:
224540 [http-bio-8080-exec-3] DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Chain org.apache.cxf.phase.PhaseInterceptorChain@586cc05e was modified. Current flow: setup [PolicyOutInterceptor] pre-logical [HolderOutInterceptor, SwAOutInterceptor, WrapperClassOutInterceptor, SoapHeaderOutFilterInterceptor] post-logical [SoapPreProtocolOutInterceptor] prepare-send [MessageSenderInterceptor] pre-stream [AttachmentOutInterceptor, StaxOutInterceptor] pre-protocol-frontend [SOAPHandlerInterceptor] write [SoapOutInterceptor] pre-marshal [LogicalHandlerOutInterceptor] marshal [BareOutInterceptor] post-marshal [LogicalHandlerOutEndingInterceptor] write-ending [SoapOutEndingInterceptor] pre-stream-ending [StaxOutEndingInterceptor] prepare-send-ending [MessageSenderEndingInterceptor]
-
Here to if we observe there is the pre-marshal phase which includes a LogicalHandlerOutInterceptor. The class is responsible for invoking our handlers for the outbound flow or the response.
224549 [http-bio-8080-exec-3] DEBUG org.apache.cxf.phase.PhaseInterceptorChain - Invoking handleMessage on interceptor org.apache.cxf.jaxws.handler.logical .LogicalHandlerOutInterceptor$LogicalHandlerOutEndingInterceptor@572b0d74 224554 [http-bio-8080-exec-3] DEBUG org.apache.cxf.jaxws.handler.HandlerChainInvoker - invoking handlers, direction: outbound 224555 [http-bio-8080-exec-3] DEBUG org.apache.cxf.jaxws.handler.HandlerChainInvoker - invoking handler of type com.ws.service.samplews_ns.handler.CrazyHandler 224555 [http-bio-8080-exec-3] INFO com.ws.service.samplews_ns.handler.CrazyHandler - call came handleMessage
Thus the call lands up in our handler's handleMessage for a second time. -
Once the chain completes processing the servlet method also ends.
224560 [http-bio-8080-exec-3] DEBUG org.apache.cxf.transport.servlet.ServletController - Finished servicing http request on thread: Thread[http-bio-8080-exec-3,5,main]
- Handlers is more generic term defined as a part of the JAX-WS specification. It concerns with filter like behavior for web services.
- CXF has implemented the above concept of filter like processing with interceptors. It also supports the more generic mechanism of handlers.
- The implementation of JAX-WS handlers in CXF is achieved using interceptors.
- This is also clear from the packages. The Handlers and related classes fall within the javax package. The interceptors and related classes land up in the cxf packages and jars.
Thanks for your article :-).
ReplyDeleteI have a very concrete doubt: In my client I receive a WSDL with a policy for usernameToken. That policy also has Nonce and Created.
Apache CXF detects policy on WSDL and launches PolicyInterceptor. Even if you are using WSS4JOutInterceptor, WSSecurityPolicyLoader will load UsernameTokenInterceptorProvider, That turns into triggering UsernameTokenInterceptor to build security header.
Problem is that UsernameTokenInterceptor don´t check WSDL policy nonce or created and therefore, doesn´t use UsernameToken "addNonce" and "addCreated" methods.
Do you have any idea how can I add Nonce and Created to security header? As I said, WSS4JOutInterceptor is not valid because UsernameTokenInterceptor is launched (and then you will have two security headers).
Regards and thanks again for you article.
Nice article bro, thx
ReplyDeleteThis is awesoome
ReplyDelete