I wanted to do essentially the same thing. My fault objects/beans are marshalled to XML just like all of the other objects passed through the WS interface. In essence, you need to do the following:
- Pull the top element of your fault and build a QName,
- Create/Add the SoapFaultDetailElement with the QName,
- Output each child element of your fault (e.g., <mycode>, <message>) to a String, and
- Insert each String into the detail element with a transform.
The following method inserts the marshalled XML into the message's fault (simply invoked from resolveExceptionInternal() in an extension of SoapFaultMappingExceptionResolver):
Code:
import javax.xml.namespace.QName;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapFaultDetail;
import org.springframework.ws.soap.SoapFaultDetailElement;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.context.SoapMessageContext;
import org.springframework.xml.transform.StringSource;
protected void addFault(SoapMessageContext theSoapContext,
CommonFault theFault)
throws TransformerException {
SoapMessage response = theSoapContext.getSoapResponse();
String msg = theFault.getMessage();
SoapFault soapFault =
response.getSoapBody().addClientOrSenderFault(msg);
// Create a fault <detail> and the single element under the detail.
// You can have multiple children of the <detail> element, but we're
// going for a single "payload" within the fault detail.
SoapFaultDetail detail = soapFault.addFaultDetail();
Element jdomFault = theFault.marshal();
QName qname = new QName(jdomFault.getNamespaceURI(),
jdomFault.getName(),
jdomFault.getNamespacePrefix());
SoapFaultDetailElement detailElt = detail.addFaultDetailElement(qname);
// Now we need to transform the CONTENTS of the fault into the element
// we just created. Problem is, we've got to skip the top-level element
// in the marshalled fault and do each of the child elements separately.
// Note: The "source" needs to be well-formed, so we can't do multiple
// children at once.
// Note: Can't add attributes to the top-level Fault element.
XMLOutputter out = new XMLOutputter(Format.getCompactFormat());
Transformer trn = TransformerFactory.newInstance().newTransformer();
for (Iterator it = jdomFault.getChildren().iterator(); it.hasNext(); ) {
String child = out.outputString((Element)it.next());
trn.transform(new StringSource(child), detailElt.getResult());
}
}
Imports are included so you can see where things are coming from. I use JDOM for marshalling, but the only trick is outputting the XML to a string to feed into the transformer as a StringSource. You would need a different approach depending on what you use to model your marshalled XML (DOM, SAX, etc).