javaspringweb-servicesjax-wsjax-ws-customization

JAX-WS spring server-side set custom fault message for RuntimeExceptions


Problem

By default, JAX-WS builds the following SOAP fault message when an uncaught exception that extends RuntimeException occurs on my server:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <S:Fault xmlns:ns3="http://www.w3.org/2003/05/soap-envelope">
         <faultcode>S:Server</faultcode>
         <faultstring>[runtime exception message here]</faultstring>
         <detail>
            <ns2:exception class="java.lang.RuntimeException" note="To disable this feature, set com.sun.xml.ws.fault.SOAPFaultBuilder.disableCaptureStackTrace system property to false" xmlns:ns2="http://jax-ws.dev.java.net/">
               <message>[runtime exception message here too]</message>
               <ns2:stackTrace>
                  [stack trace details]
               </ns2:stackTrace>
            </ns2:exception>
         </detail>
      </S:Fault>
   </S:Body>
</S:Envelope>

Which kind of make sense, except that I'd like to change that behavior in order to send that instead:

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <S:Fault xmlns:ns3="http://www.w3.org/2003/05/soap-envelope">
         <faultcode>S:Server</faultcode>
         <faultstring>Something wrong happened and it's totally our fault</faultstring>
      </S:Fault>
   </S:Body>
</S:Envelope>

Note that the message should NOT be the RuntimeException's message content, but a custom static message for any exception that extends RuntimeException that may occur server-side.

I can't change the WSDL and I don't want to set a custom exception.

I'm using the spring plugin: com.sun.xml.ws.transport.http.servlet.WSSpringServlet

How can I do that?


Solution

  • I think you can approach the problem using SoapFaultMappingExceptionResolver http://docs.spring.io/spring-ws/site/reference/html/server.html

    The SoapFaultMappingExceptionResolver is a more sophisticated implementation. This resolver enables you to take the class name of any exception that might be thrown and map it to a SOAP Fault, like so:

    <beans>
        <bean id="exceptionResolver"
            class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
            <property name="defaultFault" value="SERVER"/>
            <property name="exceptionMappings">
                <value>
                    org.springframework.oxm.ValidationFailureException=CLIENT,Invalid request
                </value>
            </property>
        </bean> </beans>
    

    The key values and default endpoint use the format faultCode,faultString,locale, where only the fault code is required. If the fault string is not set, it will default to the exception message. If the language is not set, it will default to English. The above configuration will map exceptions of type ValidationFailureException to a client-side SOAP Fault with a fault string "Invalid request", as can be seen in the following response:

    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
        <SOAP-ENV:Body>
           <SOAP-ENV:Fault>
               <faultcode>SOAP-ENV:Client</faultcode>
               <faultstring>Invalid request</faultstring>
           </SOAP-ENV:Fault>
        </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    

    If any other exception occurs, it will return the default fault: a server-side fault with the exception message as fault string.

    You should change the org.springframework.oxm.ValidationFailureException exception to the exceptions that you interested ie java.lang.Exception or java.lang.RuntimeException

    You could also create a custom exception class

    public class CustomGenericAllException extends RuntimeException {
    
    
        private String errorCode;
        private String errorMsg;
    
       //getter and setter for errorCode and errorMsg       
    
        public CustomGenericAllException(String errorCode, String errorMsg) {
            this.errorCode = errorCode;
            this.errorMsg = errorMsg;
        }
    
    }
    

    in every method you can throw this exception

     throw new CustomGenericAllException("S:Server", "Something wrong happened and it's totally our fault");
    

    and in the xml configuration you can map this generic exception <value>com.testpackage.CustomGenericAllException ....

    Hope this helps