This post shows how to log unencrypted SoapFault messages from Apache CXF in client applications. These applications use WS-Security for encryption and decryption. Note that this write-up assumes the reader is familiar with the more advanced stuff in SOAP-based web services.
Java and Apache CXF Requirements
When we wrote the codes, we used the following items. We have not tested with the later versions of the items.
- SOAP 1.2
- Java 8
- Apache CXF 2.7.18
- cxf-bundle version 2.7.18
- Encryption and decryption on the client application work
Unencrypted SoapFault Fault Sample From Apache CXF
Consider the following log fragment. It shows the stack trace of an Exception.
1 2 3 4 5 6 | com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Empty/Null NamespaceURI specified for faultCode "fc1:Sender" at com.sun.xml.internal.messaging.saaj.soap.impl.FaultImpl.setFaultCode(Unknown Source) at com.sun.xml.internal.messaging.saaj.soap.impl.FaultImpl.setFaultCode(Unknown Source) at org.apache.cxf.binding.soap.saaj.SAAJUtils.setFaultCode(SAAJUtils.java:66) at org.apache.cxf.jaxws.JaxWsClientProxy.createSoapFault(JaxWsClientProxy.java:229) at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:157) |
Usually, there are no other details logged except for the short message plus the faultCode and an encrypted SOAP response message (SoapFault) with Response-Code: 500.
Apache CXF Fault Interceptors
To log the unencrypted SoapFault messages, we must configure our org.apache.cxf.endpoint.Client
object to use interceptors to handle faults. For our purpose, we can use 2 interceptors – a custom interceptor and org.apache.cxf.binding.soap.interceptor.Soap12FaultInInterceptor
.
Using Apache CXF Soap12FaultInInterceptor
This interceptor generally sets the “Exception” content of the incoming SoapMessage object. The incoming message is basically an Exception (a Fault). See line 47 on the screenshot below.
For SOAP1.1, please use org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor
.
A Custom Apache CXF Interceptor to log Unencrypted SoapFault
In this custom interceptor, we used Phase.POST_INVOKE. For Soap12FaultInInterceptor, the codes use the “unmarshalled” String object. This means that our custom interceptor is used after the Soap12FaultInInterceptor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class CustomUnencryptedSoapFaultMessageLoggerInterceptor extends AbstractPhaseInterceptor<Message> { private static final Logger LOGGER = LoggerFactory.getLogger(CustomUnencryptedSoapFaultMessageLoggerInterceptor.class); public CustomUnencryptedSoapFaultMessageLoggerInterceptor() { super(Phase.POST_INVOKE); } public void handleMessage(Message message) { Exception exception = message.getContent(Exception.class); if(exception instanceof Fault) { Fault fault = (Fault)exception; LOGGER.error("Fault message: {}", fault.getMessage()); } } } |
Configure Apache CXF Client
We then need to configure our Client object by adding the interceptor objects to the “InFault” interceptors list.
1 2 3 4 5 | ... CustomUnencryptedSoapFaultMessageLoggerInterceptor customUnencryptedSoapFaultMessageLoggerInterceptor = new CustomUnencryptedSoapFaultMessageLoggerInterceptor(); client.getInFaultInterceptors().add(customUnencryptedSoapFaultMessageLoggerInterceptor); client.getInFaultInterceptors().add(new Soap11FaultInInterceptor()); ... |
Now, when we encounter a SoapFault in Apache CXF, we get the following sample log output.
1 2 3 | 2018-02-16 13:36:32,468 [main] CustomUnencryptedSoapFaultMessageLoggerInterceptor - Fault code: Sender 2018-02-16 13:36:32,468 [main] CustomUnencryptedSoapFaultMessageLoggerInterceptor - Fault message: Invalid File Format: Sender is not allowed for non-registered account. |