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:
- Using WS-Addressing decoupled response
- 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)
- The consumer implementation invokes an operation and a request message is generated.
- 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.
- The message is sent to the service provider.
- The service provider receives the message.
- The request message from the consumer is dispatched to the provider's WS-A layer.
- 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.
- 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.
- When the consumer receives the 202 Accepted reply, the HTTP connection closes.
- The request is passed to the service provider's implementation where the request is processed.
- When the response is ready, it is dispatched to the WS-A layer.
- The WS-A layer adds the WS-Addressing headers to the response message.
- The HTTP transport sends the response to the consumer's decoupled endpoint.
- The consumer's decoupled endpoint receives the response from the service provider.
- 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.
- 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
>
</
jaxws:features
>
</
jaxws:endpoint
>
<
jaxws:
client id="{your.service.namespace}YourPortName">
<
jaxws:features
>
</
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:- Creates empty response message (partial response)
- Initializes new interceptor chain: adds all registered interceptors for endpoint, service, bus and binding
- Replaces interceptor chain to the new one
- Creates and replaces ConduitSelector in exchange to PreexistingConduitSelector with original back channel
- Invokes chain.doIntercept() and sends empty response (HTTP status 202)
- Resets the interceptors chain
- Creates and initializes new message for real response
- Creates decoupled destination for the client
- Suspends current interceptor chain for full response and resumes that in new thread
- Service handler will be called from ServiceInvokerInterceptor in new thread and response will be delivered to decoupled destination
Samples
I have created two samples illustrating decoupled responses on Git Hub respository:
- Simple scenario based on Java First client and service
- 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.
Samples
Sample illustrating using of independent oneway operations is uploaded to Git Hub repository