Catching the SOAP Fault error in custom interceptor (Soap12FaultOutInterceptor)

21,845

Solution 1

So in my custom interceptor I write the following code:

Fault fault = new Fault(message.getContent(Exception.class));

Now this is in some legacy code that was throwing exceptions from Java and letting the framework convert it to a fault. I won't get into my feelings on that, but this will get you the fault that is generated.

Now if you are throwing a fault from your service method, do

Fault fault = message.getContect(Fault.class);

Hopefully this will help you get the answer to what you want. Make sure you register the interceptor like below

<jaxws:endpoint
  id="fooService" implementor="com.bar.FooService" address="/FooServices">
  <jaxws:outFaultInterceptors>
        <ref class="com.bar.interceptor.DbLogOutInterceptor"/>
  </jaxws:outFaultInterceptors>
</jaxws:endpoint>
<jaxws:endpoint

Solution 2

The Best way is to implement the Fault listener and use org.slf4j.Logger to log the error message instead of using java logging.

It would be wise to override the LoggingInInterceptor and write ur custome interceptor to get the request payload.

public class CxfInputFaultInterceptor extends AbstractLoggingInterceptor {
private static final Logger LOG = LogUtils.getLogger(CxfInputFaultInterceptor.class);

public CxfInputFaultInterceptor() {
    super(Phase.RECEIVE);
}

Step 1: cxf-beans.xml

    <cxf:bus>
    <cxf:inInterceptors>
        <ref bean="cxfInputFaultInterceptor"/>
    </cxf:inInterceptors>
    <cxf:inFaultInterceptors>
        <ref bean="cxfInputFaultInterceptor"/>
    </cxf:inFaultInterceptors>
    <cxf:properties>
        <entry key="org.apache.cxf.logging.FaultListener">
            <bean id="cxfFaultListener" class="pkg.common.ws.interceptor.CxfFaultListenerImpl" >
                <property name="loggedUser" ref="loggedUser"/>
            </bean> 
        </entry>
    </cxf:properties>
</cxf:bus>

Step 2 : your listener which should implements org.apache.cxf.logging.FaultListener

import java.io.InputStream;

import org.apache.cxf.interceptor.LoggingMessage;
import org.apache.cxf.logging.FaultListener;
import org.apache.cxf.message.Message;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.service.model.InterfaceInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * Listener to faults on the processing of messages by CXF intercepter chain. Here we      override 
 * java.util.logging.Logger and use  org.slf4j.Logger to print error to console.
 */

public class CxfFaultListenerImpl implements FaultListener{

private static final Logger logger = LoggerFactory.getLogger(CxfFaultListenerImpl.class);
private static final String NEWLINE="\n";

public boolean faultOccurred(final Exception exception,final String description,final Message message) {

    createErrorLog(message);
    logger.error(" --------------------------------------------------------------------------------------------------");
    logger.error(" Stack Trace  :         ");
    logger.error(" --------------------------------------------------------------------------------------------------");
    logger.error(NEWLINE,exception);
    logger.error(" --------------------------------------------------------------------------------------------------");

    return true;
}

private void createErrorLog(final Message message){

     final Message inMessage = message.getExchange().getInMessage();

     final InputStream is = inMessage.getContent(InputStream.class);

     final EndpointInfo endpoint = message.getExchange().getEndpoint().getEndpointInfo();
     String logName=null;

     if(endpoint!=null && endpoint.getService()!=null){
         final String serviceName = endpoint.getService().getName().getLocalPart();
         final InterfaceInfo iface = endpoint.getService().getInterface();
         final String portName = endpoint.getName().getLocalPart();
         final String portTypeName = iface.getName().getLocalPart();
         logName =  serviceName + "."  + portName + "." + portTypeName;
     }
    final LoggingMessage buffer
    = new LoggingMessage("Error occured on Service Call  : "+NEWLINE +"----------------------------", logName);

    final Integer responseCode = (Integer)message.get(Message.RESPONSE_CODE);
    if (responseCode != null) {
        buffer.getResponseCode().append(responseCode);
    }

    final String encoding = (String)message.get(Message.ENCODING);

    if (encoding != null) {
        buffer.getEncoding().append(encoding);
    }
    final String httpMethod = (String)message.get(Message.HTTP_REQUEST_METHOD);
    if (httpMethod != null) {
        buffer.getHttpMethod().append(httpMethod);
    }
    final String ct = (String)message.get(Message.CONTENT_TYPE);
    if (ct != null) {
        buffer.getContentType().append(ct);
    }
    final Object headers = message.get(Message.PROTOCOL_HEADERS);

    if (headers != null) {
        buffer.getHeader().append(headers);
    }
    final String uri = (String)message.get(Message.REQUEST_URL);
    if (uri != null) {
        buffer.getAddress().append(uri);
        final String query = (String)message.get(Message.QUERY_STRING);
        if (query != null) {
            buffer.getAddress().append("?").append(query);
        }
    }

    final String requestXml= is.toString();
    if(requestXml !=null){
        buffer.getMessage().append("LoggedIn User:  ");
        buffer.getMessage().append(getCurrentUsername()+NEWLINE);
        buffer.getMessage().append("Request payload  : "+NEWLINE);
        buffer.getMessage().append(requestXml);
    }else{
        buffer.getMessage().append("LoggedIn User:  ");
        buffer.getMessage().append(getCurrentUsername()+NEWLINE);
        buffer.getMessage().append("No inbound request message to append.");
    }

    logger.error(buffer.toString());
}

}

Hope that helps someone who just wants the stack trace and payload only when an error occurs on the service call and thus avoid huge log files.

Solution 3

To capture faults, you need to register the interceptor as a fault interceptor. For example

<cxf:outFaultInterceptors>
   <bean class="DbLogOutInterceptor" />
</cxf:outFaultInterceptors>

See the CXF Configuration page under "Enabling message logging using custom CXF bean elements" for an example using the CXF logging interceptors to capture in/out messages and in/out faults.

Share:
21,845
NullPointerException
Author by

NullPointerException

Writing software code and developing apps. Supporter of open source technologies. There is nothing like spreading knowledge without charging !!

Updated on March 26, 2020

Comments

  • NullPointerException
    NullPointerException about 4 years

    I wrote a custom CXF interceptor to log all the SOAP request and responses into the database and it seems to be working fine with positive test cases and server errors.

    But when a SOAP Fault occurs it just neglects my interceptor and nothing is logged.

    Custom CXF interceptors.

    public class DbLogOutInterceptor extends AbstractSoapInterceptor {
    
     public void handleMessage(SoapMessage message) {
        logger.info("Handling outbound request");
    
        String transaction = MDC.get(mdcKey);
        logger.info("OutBound Transaction ID : {} ", transaction);
    
         //code to log the SOAP message in database
        .......
    
         }
       }
    

    I am not seeing the log statements from this method instead I see

     11:56:34,102 INFO  [Soap12FaultOutInterceptor] class org.apache.cxf.binding.soap.interceptor.Soap12FaultOutInterceptor$Soap12FaultOutInterceptor Internalapplication/soap+xml
     11:56:34,103 INFO  [EligibilityInfo] Outbound Message
     ---------------------------
     ID: 2
     Response-Code: 500
     Encoding: UTF-8
     Content-Type: application/soap+xml
     Headers: {}
     Payload :  
    

    What I have to do in order to capture the SOAP fault erros in my custom interceptors.