Consider Asynchronous SOAPServices as Synchronous using Camel CXF
Earlier I wrote about consuming a SOAP Service, and handling SOAP Faults. These stories talk about using Synchronous Webservices.
Message Exchange Patterns
When considering SOAP Webservices, there are several so-called Message Exchange Patterns:
- Synchronous: this is the most obvious pattern. You do a request, and in the same call, you expect a response from your service. The Service Provider receives the request and processes it, while the Service Consumer waits for the response. The response can be the expected response message, but also one from several possible SOAP Faults. But, a SOAP Fault is in essence a valid SOAP response message.
In a WSDL you can recognize this in WSDL operations that have both an input and an output message and possibly one or more possible Fault Messages. - Asynchronous Fire & Forget: most people consider this as asynchronous. But, we have another asynchronous pattern. Fire & Forget services are invoked, but you won’t get a response. The Service Provider reads the request and immediately responds with an HTTP-200 response code, unless not authenticated, authorized, incorrect URI, etc.
You can recognize this in a WSDL when there is one port type with an operation that only has an input message. - Asynchronous Request & Response: this pattern is actually a combination of two complementary Fire & Forget services. The service invoker calls the service provider in a Fire & Forget way. It immediately gets an OK (HTTP-200) or Not OK (any other HTTP response code) and continues processing. In parallel, the service consumer started an endpoint to listen for the response. This endpoint is provided in a ReplyTo Address WS-Addressing SOAP Header element in the request. Together with a referencing MessageId. When done, the Service Provider can invoke that address with the response. In a RelatesTo element, it can provide the given MessageId to indicate for which request this is an answer.
You can recognize this in a WSDL when there are two port types, both with an operation that only has an input message. One port type is meant for the Service Consumer, the invoking process. The other is the callback port type used by the Service Provider, to invoke the Service Consumer with the response.
The Problem
If due to a time out or a Connection Refused exception, for instance, the invocation of a synchronous service fails, you won’t get a response. It is quite easily recognized. Handling errors like that is not a real problem with synchronous services.
However, with asynchronous services, it is not that obvious. You won’t get a response. But, you do want to be sure that the remote service is invoked successfully. However, CXF turns out to handle the invocation asynchronously. But, with a connection refused (the remote service is not reachable) CXF logs the exception, but for Camel, it acts as if nothing happened.
I tend to mix up the Camel concepts of Consumer and Producer. But, a Consumer is a component that you reference in a from-activity. An incoming message is wrapped into an Exchange and delivered to the Camel route for processing. A Producer is the component that you reference in a to-activity and can be used to send messages. When I talk about a Camel route that acts as a Service Consumer invoking a WebService, then the route references that WebService as a producer.
You can set an ExceptionHandler as a Consumer (Advanced) property:
This will not do anything, since we don’t use the reference as a consumer.
The Solution
There is a Producer (Advanced) CXF property that helps in this case:
As described above, this suggests that by default CXF will handle an service invocation asynchronously. So, the Camel route will process further apart from the CXF call.
But, when this property is set to true the processing is done synchronously.
Now, in case of a Connection Refuse an exception will pop-up in Camel:
Conclusion
In the case of asynchronous services, you’ll probably set the synchronous property in all or most situations. In other words: handle asynchronous service calls synchronously. Doing so, you’ll be able to catch exceptions that raise during the call.