Earlier I wrote about how to consume a SOAP Service in a Fuse/Camel integration using the CXF framework. But how about SOAP Faults?
The processing of a service can result in an error. There are several types of errors, of course. You could fail in calling the service. Because the service is down or unavailable, because of a network outage. But if you look up an order with an invalid id, did the service then fail? I don’t think so: the service did its job perfectly fine, it worked. Not being able to find a non-existing order can be a proper answer.
There are several ways to provide an answer like that to the service consumer. The service could answer with an empty response. Or with a message with a status code that denotes a not-found situation. In those cases, the service design defines a not-found situation as a proper output. Another way is to respond with a SOAP Fault. Now, the term Fault can be misleading. You can define the not-found situation as an exception. Because, how did you come up with its specific id? There was a functional reason to ask for an order with that id, so you probably got that order id from an earlier integration.
Still, if you ask for an order with an id that should be valid, but did not produce a factual order, the service did its job properly. A SOAP Fault in this case is a perfect response and does not mean that the service failed. And therefor a SOAP Fault can be used for both technical as functional (business) exceptions.
I find the example above important because it explains why a SOAP Fault response would not lead to an Exception in Camel. You need to check for it.
Investigating the CXF SoapMessage
In the AnimalOrderSOAP project, I have a processor called LogHeadersProcessor. If you invoke this after invoking a SOAP Service using CXF, you’ll see that one of the Header variables is “CamelCxfMessage”. This message is in fact a org.apache.cxf.binding.soap.SoapMessage. And that is an implementation of the org.apache.cxf.message.Message interface. But in the end, it turns out to be a HashMap<String, Object>. And it uses key-values based on class names. The Message interface also defines several constants for the particular elements that CXF registers in the HashMap. One of them is RESPONSE_CODE that is defined as:
The actual key for the RESPONSE_CODE is: “org.apache.cxf.message.Message.RESPONSE_CODE”.
Checking the SOAP Fault the Java way
You could parse the CamelCxfMessage as in the following snippet:
This example shows how you would get the RESPONSE_CODE out of the CXF Message. An HTTP Response Code of 500 would denote a SOAP Fault or (Internal) Service error. An HTTP Response Code of 200 means success. Another famous one is 404, which means “not found”.
When there is an HTTP Response Code of 500, you could check the content using an Xpath expression like “//*[local-name()=’Fault’]/faultstring/text()” to get the actual fault message from the SOAP Fault.
Using Simple in XML DSL (Spring or Blueprint)
The Java approach seems straightforward. And is probably the most obvious choice for many. But it would mean that you need to define another bean with a specific method that implements the code above. And then based on the outcome, you would need to control your processing flow.
The knowledge of the CXF SoapMessage, can also be translated to a Simple expression as follows:
Using this header variable you can define a choice-element with a when/otherwise construct to handle your flow:
This snippet assumes that HTTP ResponseCode 200 means success and in all other cases you probably got a SOAP Fault. There are a lot of values this snippet omits. In most cases, this would suffice.
The example above shows how you can get the HTTP Response code from the CXF Message. This works for a CXF REST Implementation just the same of course. And for all the other possible CXF properties.
I love that being able to get these properties using a Simple expression I don’t need another bean/method and can do the complete handling within XML DSL. And as you may know already: I love the declarative way. I only might want to register these possible CXF constants in a property file. That would make the simple expressions more compact. I‘ll try that in a next implementation.