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

2

7

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?

Rosaleerosaleen answered 24/6, 2016 at 16:22 Comment(3)
Right in exception tag there is an attribute note that says 'To disable this feature, set com.sun.xml.ws.fault.SOAPFaultBuilder.disableCaptureStackTrace system property to false'. Did you try that? Did you try to find out whether there is a property to disable entire details?Vestry
Yeah, and that's not really what I want.Rosaleerosaleen
Isn't this similar to #31069614 ?Schoolroom
R
1

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

Rafaellle answered 27/6, 2016 at 11:57 Comment(2)
I read about SoapFaultMappingExceptionResolver in the past, but I have troubles finding the correct version of Spring WS (I'm on JDK6 with Spring 3.1.2 packages)Rosaleerosaleen
Can you use spring ws 2.1.4? github.com/spring-projects/spring-wsRafaellle
E
0

I am assuming you have an endpoint similar to the one below:

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.soap.SoapFaultException;
import org.w3c.dom.Element;

@Endpoint
public class HolidayEndpoint {

    private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";

    public HolidayEndpoint() {
    }

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")                  
    public void handleHolidayRequest(@RequestPayload Element holidayRequest)               
        throws Exception {
      //your code to handle the request on the server
    }
}

Now, let's say handleHolidayRequest() is where you anticipate the RuntimeException to happen, the would have to change the code like so:

import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.soap.SoapFaultException;
import org.w3c.dom.Element;

@Endpoint
public class HolidayEndpoint {

    private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";

    public HolidayEndpoint() {
    }

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")                  
    public void handleHolidayRequest(@RequestPayload Element holidayRequest)               
        throws Exception {
        try
        {
            //your code to handle the request on the server
        }
        catch(RuntimeException e)
        {
            String faultMessage = "Something's wrong on our end. Try again later.";
            throw new SoapFaultException(faultMessage);
        }
    }
}

Note how I catch the runtime exception and throw an new SoapFaultException? That does the trick and the response is like so:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <SOAP-ENV:Fault>
         <faultcode>SOAP-ENV:Server</faultcode>
         <faultstring xml:lang="en">Something's wrong on our end. Try again later.</faultstring>
      </SOAP-ENV:Fault>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Now, if you are using something like spring-integration with a service activator, it's just the same concept except instead of throw the SoapFaultException, you are just going to build a message with the SoapFaultException as the payload:

@ServiceActivator(inputChannel = "input", outputChannel = "output")
public Message<?> handleHolidayRequest(HolidayRequest holidayRequest) {
   try {
      // your code to handle the request on the server
   } catch (RuntimeException e) {
      String faultMessage = "Something's wrong on our end. Try again later.";
      SoapFaultException soapFaultException = new SoapFaultException(faultMessage);
      return MessageBuilder.withPayload(soapFaultException).build();
   }
}

PS: For your understanding, I used the web service tutorial example from here for illustration (and I do have a working example if you are still struggling): http://docs.spring.io/spring-ws/site/reference/html/tutorial.html

Good luck!

Electrocautery answered 30/6, 2016 at 13:14 Comment(2)
Man, I think you're kind of off topic because of this particular sentence : "I can't change the WSDL and I don't want to set a custom exception". What I want is to change the default exception to soap fault behavior.Rosaleerosaleen
Ok, understood. I am glad that SoapFaultMappingExceptionResolver worked out for you.Electrocautery

© 2022 - 2024 — McMap. All rights reserved.