Monday, 3 March 2014

Asynchronous Communication with HTTP

Requirements and Solutions

Sometimes using of native asynchronous transports like JMS is not possible because of technical or political reasons, but asynchronous communication is required anyway.
CXF does have a facilities to help with these types of requirements:
  1. Using WS-Addressing decoupled response
  2. Using independent oneway operations

WS-Addressing decoupled response

Decoupled response uses different HTTP channel to send the response. Client in this case acts as a service as well: it opens independent HTTP connection and can receives the messages. The communication is shown on the following picture (used from FuseSource resource)

  1. The consumer implementation invokes an operation and a request message is generated.
  2. The WS-Addressing layer adds the WS-A headers to the message. When a decoupled endpoint is specified in the consumer's configuration, the address of the decoupled endpoint is placed in the WS-A ReplyTo header.
  3. The message is sent to the service provider.
  4. The service provider receives the message.
  5. The request message from the consumer is dispatched to the provider's WS-A layer.
  6. Because the WS-A ReplyTo header is not set to anonymous, the provider sends back a message with the HTTP status code set to 202, acknowledging that the request has been received.
  7. The HTTP layer sends a 202 Accepted message back to the consumer using the original connection's back-channel. The consumer receives the 202 Accepted reply on the back-channel of the HTTP connection used to send the original message.
  8. When the consumer receives the 202 Accepted reply, the HTTP connection closes.
  9. The request is passed to the service provider's implementation where the request is processed.
  10. When the response is ready, it is dispatched to the WS-A layer.
  11. The WS-A layer adds the WS-Addressing headers to the response message.
  12. The HTTP transport sends the response to the consumer's decoupled endpoint.
  13. The consumer's decoupled endpoint receives the response from the service provider.
  14. The response is dispatched to the consumer's WS-A layer where it is correlated to the proper request using the WS-A RelatesTo header.
  15. The correlated response is returned to the client implementation and the invoking call is unblocked.
 Configuring decoupled response is trivial: it is necessary to activate WS-Addressing Feature on client and service and provide ReplyTo and FaultTo addresses on the client:

Activation of WS-Addressing Feature

For <jaxws:endpoint>:
<jaxws:endpoint id="{your.service.namespace}YourPortName">
  <jaxws:features>
    <wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
  </jaxws:features>
</jaxws:endpoint>
For <jaxws:client>:
<jaxws: client id="{your.service.namespace}YourPortName">
  <jaxws:features>
    <wsa:addressing xmlns:wsa="http://cxf.apache.org/ws/addressing"/>
  </jaxws:features>
</jaxws:client>

Configuring WSA Properties

Client should configure ReplyTo WS-Addressing property to receive decoupled response:
((BindingProvider)proxy).getRequestContext()
    .put("org.apache.cxf.ws.addressing.replyto", "http://localhost:9090/decoupled_endpoint");
Alternatively, client can use CXF AddressingPropertiesImpl object to control many aspects of WS-Addressing including ReplyTo:
AddressingProperties maps = new AddressingPropertiesImpl();
EndpointReferenceType ref = new EndpointReferenceType();
AttributedURIType add = new AttributedURIType();
ref.setAddress(add);
maps.setReplyTo(ref);
maps.setFaultTo(ref);
  
((BindingProvider)proxy).getRequestContext()
    .put("javax.xml.ws.addressing.context", maps);

Under the Hood

CXF makes the following steps on the service side by receiving the client request:
  1. Creates empty response message (partial response)
  2. Initializes new interceptor chain: adds all registered interceptors for endpoint, service, bus and binding
  3. Replaces interceptor chain to the new one
  4. Creates and replaces ConduitSelector in exchange to PreexistingConduitSelector with original back channel
  5. Invokes chain.doIntercept() and sends empty response (HTTP status 202)
  6. Resets the interceptors chain
  7. Creates and initializes new message for real response
  8. Creates decoupled destination for the client
  9. Suspends current interceptor chain for full response and resumes that in new thread
  10. Service handler will be called from ServiceInvokerInterceptor in new thread and response will be delivered to decoupled destination
Note, that CXF automatically cares about all communication aspects: opens client endpoints, sends responses to the correct endpoint, correlates the requests and response.

Samples

I have created two samples illustrating decoupled responses on Git Hub respository:
  1. Simple scenario based on Java First client and service
  2. Dispatching scenario based on Provider<T> service and Dispatch client containing code to dispatch responses to registered callbacks

Independent Oneway Operations

WS-Addressing decoupled response is very useful for most of cases, but sometimes you need more flexibility. For example I have seen the requirements to support more than one response to single client request (multi-response conversation) or to persist service state during request processing. In this case you can consider to use two independent oneway operations: one on the service side and second on the client side:
The client registers own endpoint and uses it to receive responses from the service. This solution is very flexible, you can fulfill requirements like:
  • receive some responses for the same request;
  • dispatch responses to different handlers on the client side;
  • persist service state after receiving request, shutdown the service, start up it again when response is ready and send response to client endpoint.
The downside of this flexibility is that approach is more involved in implementation: you should explicitly register endpoint on the client side, send responses on the service side using client, correlate request and response messages.

Samples

Sample illustrating using of independent oneway operations is uploaded to Git Hub repository

No comments:

Post a comment