Of ExceptionResolvers and XMLBeans

We’re using XMLBeans with Spring Web Services, and the set-up was quite easy. But the one issue I found frustrating was handling the SoapFault. The requirements in this case required including a SoapFaultDetail that contained a complex type. And while the XmlBeansMarshaller transparently handles transforming the incoming and outgoing SOAP requests, it didn’t magically include the type defined as the fault in the WSDL within the SoapFault message.

Digging through the tutorial and some online forum postings, I finally found a solution. The AbstractSoapFaultDefinitionExceptionResolver provides a protected method void customizeFault(Object endpoint, Exception ex, SoapFault fault) that subclasses can override to modify the SoapFault before it is returned. Since this method includes the exception as a parameter, I chose to create an exception that is thrown by my endpoints that includes my desired XmlBean, already populated, so I can just use SoapFault.getResult() to marshall the information into the details.

For simplicity’s sake, I chose to override the existing SoapFaultMappingExceptionResolver.

Below, ServiceFaultDocument is an XmlObject generated by XMLBeans from my schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"  xmlns:s1="http://stevideter.com/webservice">
    <xs:element name="ServiceFault" type="s1:ServiceFault"/>
    <xs:complexType name="ServiceFault">
        <xs:sequence>
            <xs:element name="errorCode" type="xs:string" minOccurs="0" maxOccurs="1"/>
	    <xs:element name="errorDescription" type="xs:string" minOccurs="0" maxOccurs="1"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

For my Exception class:

package com.stevideter.webservice;
 
import org.springframework.ws.soap.server.endpoint.annotation.FaultCode;
import org.springframework.ws.soap.server.endpoint.annotation.SoapFault;
 
import com.stevideter.webservice.ServiceFaultDocument;
/**
 * An exception that carries a ServiceFault document for inclusion in 
 * the SoapFault
 * @author stevi.deter
 *
 */
@SoapFault(faultCode = FaultCode.SERVER,faultStringOrReason="SERVICE-ERR",locale="en")
public class ServiceFaultException extends Exception {
 
	private static final long serialVersionUID = 1L;
	private ServiceFaultDocument faultMessage;
 
	public ServiceFaultException() {
		super("ServiceFaultException");
	}
 
	public ServiceFaultException(String s) {
		super(s);
	}
 
	public ServiceFaultException(String s, Throwable ex) {
		super(s, ex);
	}
 
	public ServiceFaultException(String s, Throwable ex, ServiceFaultDocument msg) {
		super(s,ex);
		setFaultMessage(msg);
 
	}
 
	public void setFaultMessage(ServiceFaultDocument msg) {
		faultMessage = msg;
	}
 
	public ServiceFaultDocument getFaultMessage() {
		return faultMessage;
	}
 
}

And finally my ExceptionResolver. Note that logger is ultimately inherited from AbstractEndpointExceptionResolver, which is why you don’t see it declared in the code displayed.

package com.stevideter.webservice.soap.server.endpoint;
 
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
 
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapFaultDetail;
import org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver;
import org.springframework.xml.transform.StringSource;
 
import com.stevideter.webservice.ServiceFaultException;
import com.stevideter.webservice.ServiceFaultDocument;
 
public class DetailSoapFaultDefinitionExceptionResolver extends
		SoapFaultMappingExceptionResolver {
 
	@Override
	protected void customizeFault(Object endpoint, Exception ex, SoapFault fault) {
		ServiceFaultException msg = null;
		if (ex instanceof ServiceFaultException) {
			msg = (ServiceFaultException) ex;
		} else {
			msg = createFaultMessage(ex);
		}
		addServiceFaultDetail(msg, fault);
	}
 
	private void addServiceFaultDetail(ServiceFaultException msg, SoapFault fault)
			throws TransformerFactoryConfigurationError {
		Transformer trn;
		try {
			trn = TransformerFactory.newInstance().newTransformer();
			SoapFaultDetail faultDetail = fault.addFaultDetail();
			Result result = faultDetail.getResult();
			ServiceFaultDocument doc = msg.getFaultMessage();
			if (doc == null) {
				logger.error("ServiceFaultException thrown with no serviceFaultDocument!",msg);
			} else {
				trn.transform(new StringSource(doc.toString()), result);
			}
		} catch (TransformerException e) {
			logger.error("problem with XML transform: ", e);
		}
	}
 
	private ServiceFaultException createFaultMessage(Exception e) {
		ServiceFaultDocument faultDocument = ServiceFaultDocument.Factory.newInstance();
		ServiceFault fault = faultDocument.addNewServiceFault();
		fault.setErrorCode("SERVICE-ERR");
		fault.setErrorDescription(e.getMessage());
		ServiceFaultException faultMsg = new ServiceFaultException(e.getMessage(),e,faultDocument);
		return faultMsg;
	}
}

The final step is injecting my ExceptionResolver in my webservice’s servlet.xml; just showing the single bean definition here:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<!-- rest of web service bean def's elided -->
 
    <bean id="exceptionResolver"
	        class="com.stevideter.webservice.soap.server.endpoint.DetailSoapFaultDefinitionExceptionResolver">
        <property name="defaultFault" value="SERVER"/>
        <property name="exceptionMappings">
            <value>
	    com.stevideter.webservice.ServiceFaultException=SERVER,FaultMsg
            </value>
	</property>
    </bean>
</beans>

Now whether my Endpoints throw an Exception, the ExceptionResolver transforms it into the SoapFaultDetail, and the calling system get the results they desire:

<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">FaultMsg</faultstring>
         <detail>
            <ServiceFault xmlns="http://stevideter.com/webservice">
               <errorCode xmlns="">SERVICE-ERR</errorCode>
               <errorDescription xmlns="">you can't do that!</errorDescription>
            </ServiceFault>
         </detail>
      </SOAP-ENV:Fault>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

One issue I discovered in developing this was that we were using Saxon 8.8, a version which apparently has a bug that throws an exception on the empty namespaces in the Service Fault elements during the transform. According to the information I found, upgrading to Saxon 8.9 fixes that; I went ahead and upgraded to Saxon 9.1.0.5 (current version as I write this) and the problem went away. Be sure to include saxon9.jar and saxon9-dom.jar if you go this route!

How have you used ExceptionResolvers in Web Services?

19 thoughts on “Of ExceptionResolvers and XMLBeans

  1. Osman Sebati

    Do you know, How can we exclude some of our endpoints, from this (Exception to SoapFaultDetail) mapping?

    I mean, How can we expilicitely include specific endpoints to this ExceptionResolving issue?

    By the way it is useful and great post. Thanx.

  2. Joris Vleminckx

    I solved it this way:

    created a class extending org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver
    and overriding the template method customizeFault() as follows to add a custom errorCode in the soapfaultdetail part:

    @Override
    protected void customizeFault(MessageContext messageContext, Object endpoint, Exception ex, SoapFault fault) {
    SoapFaultDetail soapFaultDetail = fault.addFaultDetail();
    Result result = soapFaultDetail.getResult();
    try {
    Transformer trn = TransformerFactory.newInstance().newTransformer();
    trn.transform(new StringSource(“” + ((MyException) ex).getErrorCode() + “”), result);
    } catch (TransformerException e) {
    e.printStackTrace();
    logger.error(“Error transformando: ” + e.getMessage());
    }
    }

    This leads to the following soapfault:

    SOAP-ENV:ServerMy error message24

  3. Sri

    Stevi
    if you have a servicefaultdocument sample that you can post that will be helpful, eventhough it’s generated by beans.
    this blog will be complete with that info and readers can implement any way they want.
    By the way, this is absolutely a GREAT posting !!
    Thanks a lot !

  4. Scott Haynie

    Awesome post… I followed it to add the exception stack trace to my soap fault message, cause I couldn’t figure out how to get Spring to do that. Thanks!

  5. Naren

    Hi,
    Firstly, thanks for such a wonderful article. Can I ask you what prompted you to contain exception object inside SOAP Fault? Doing so, web services may not be interoperable with other languages or platforms. I think the best way to fulfill your need and keeping web service portable is to have more Fault attributes and if you really need Throwable (I don’t see why), you can see String format of Stack Trace, instead of sending whole Throwable object.

    Cheers,
    Naren

  6. stevi Post author

    Naren,
    If you look at the example of the resulting SOAP-ENV:Fault, you’ll see there is no exception object carried into the web service.

    Instead, I’ve used the technique described to translate my specific Java exception into the complex type the web service contract is expecting.

  7. Yang

    Hi,
    I am confused by the ServiceFaultDocument that you mention that is an XmlObject generated by XMLBeans from the schema. How do I generate the ServiceFaultDocument Xmlobject with the schema? Pls advice…Thank you in advance and sorry if I’m asking a noive question.

  8. Yang

    Sorry…in addition, I saw the following steps in the xmlbeans link that you mention in your previous post:

    If you want to get right to it with your own XML schema and instance, follow these basic steps:
    1) Install XMLBeans.
    2) Compile your schema. Use scomp to compile the schema, generating and jarring Java types. For example, to create a employeeschema.jar from an employeesschema.xsd file:

    scomp -out employeeschema.jar employeeschema.xsd

    3) Write code. With the generated JAR on your classpath, write code to bind an XML instance to the Java types representing your schema. Here’s an example that would use types generated from an employees schema:

    File xmlFile = new File(“c:\employees.xml”);

    // Bind the instance to the generated XMLBeans types.
    EmployeesDocument empDoc =
    EmployeesDocument.Factory.parse(xmlFile);

    // Get and print pieces of the XML instance.
    Employees emps = empDoc.getEmployees();
    Employee[] empArray = emps.getEmployeeArray();
    for (int i = 0; i < empArray.length; i++)
    {
    System.out.println(empArray[i]);
    }

    Is this what I am suppose to do? As from what I understand, it will create a jar file but where does the EmployeesDocument datatype come from? Pls correct me and advice if I'm wrong.

  9. Ibrahim Rashid

    Very informative post!!!!

    The way you explained the entire flow is fantastic.. Thanks a lot for your post.

    I too have to insert the custom fault message to the details tag of SOAP:Fault, I googled a lot and finally stopped by your amazing post…That’s what now I am going to do…

    How did the XMLBeans generate the ServiceFaultDocument class but in XSD, the element is mentioned as ServiceFault?

    Just one request!!!!
    Can u please post the ServiceFaultDocument java class, I know it’s a generated class, just for the sake curiosity, please post it.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>