JAX-WS spring custom server-side error message for RuntimeExceptions

Problem

By default, JAX-WS generates the following SOAP error message when an uncaught exception occurs on my server that propagates to a RuntimeException :

 <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> 

What is the point of making sense, except that I would like to change this behavior to send this 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 totally our fault</faultstring> </S:Fault> </S:Body> </S:Envelope> 

Note that the message must be NOT the contents of the RuntimeException message, but a custom static message for any exception that extends the RuntimeException that may occur on the server side.

I cannot change the WSDL, and I do not want to set a custom exception.

I am using spring plugin: com.sun.xml.ws.transport.http.servlet.WSSpringServlet

How can i do this?

+6
source share
2 answers

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 complex implementation. This resolver allows you to take the class name any exception that may be thrown and map it to a SOAP error, for example like this:

 <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 and endpoint defaults to the faultCode, faultString, locale format, where only the fault code is required. If the fault line is not set, a message exception will be thrown by default. If no language is specified, it will default to English. client-side ValidationFailureException type exceptions will be displayed above the configuration SOAP error with error string "Invalid request", as seen from the following answer:

 <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 a default error: a server error with an exception message as a failure string.

You should change the org.springframework.oxm.ValidationFailureException exception for those exceptions that you are interested in, i.e. java.lang.Exception or java.lang.RuntimeException

You can also create your own 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 each method you can throw this exception

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

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

Hope this helps

0
source

I assume 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 expect a RuntimeException to occur, you will need to change the code as follows:

 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 wrong on our end. Try again later."; throw new SoapFaultException(faultMessage); } } } 

Notice how I catch the runtime exception and throw a new SoapFaultException? This does the trick, and the answer is:

 <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 wrong on our end. Try again later.</faultstring> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope> 

Now, if you use something like spring integration with a service activator, this is just the same concept, but instead of throwing a SoapFaultException, you just collect the message with SoapFaultException as a 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 wrong on our end. Try again later."; SoapFaultException soapFaultException = new SoapFaultException(faultMessage); return MessageBuilder.withPayload(soapFaultException).build(); } } 

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

Good luck

0
source

All Articles