Hello Dan and all, Ok, the SAAJOutInterceptor solution almost worked. The behavior I describe below is the same in CXF 2.2.4 and 2.2.6-SNAPSHOT (as of Saturday's build). I am not on 2.2.5 due to some bugs that are fixed in 2.2.6. The debugging information I gathered for this message is with 2.2.4.
I have two scenarios, one works and one does not with the SAAJOutInterceptor solution. If a Fault is thrown by a custom interceptor at a certain point in the output chain, the wrong SOAP XML is generated, specifically no SOAP fault XML elements are generated. Let me start by showing what works and why before showing what does not and why. In the first scenario, which works, a SoapFault is thrown by our provider's invoke(SOAPMessage) method under certain conditions, basically if our server detects certain errors. Our provider looks like this: @WebServiceProvider @ServiceMode(value = Service.Mode.MESSAGE) public static class LdeWebServiceProvider implements Provider<SOAPMessage> { @Override public SOAPMessage invoke(SOAPMessage soapRequest) { ... throw new SoapFault("Our message", Soap11.getInstance().getReceiver()); } } In SAAJOutInterceptor.handleMessage, the local variable saaj is null, so the code path taken builds a SOAP message from scratch and plugs in a W3CDOMStreamWriter. All of the interceptors then write to W3CDOMStreamWriter instead of the default XMLStreamWriter which normally caches and writes to the HTTP wire. The W3CDOMStreamWriter allows me to transform the DOM before it gets on the wire. Great stuff, it works. In the second scenario, I test our output feature where in addition to an optional transformation, we have custom interceptors to do optional XML validation before and after the XML transformation. After our provider successfully processed a message, the output chain processing kicks in and looks like this: Chain org.apache.cxf.phase.phaseinterceptorch...@c375934. Current flow: setup [PolicyOutInterceptor] pre-logical [SwAOutInterceptor, SoapHeaderOutFilterInterceptor] post-logical [SoapPreProtocolOutInterceptor] prepare-send [MessageSenderInterceptor, MessageModeOutInterceptor] pre-stream [LoggingOutInterceptor, XmlDeclOutInterceptor*, AttachmentOutInterceptor, StaxOutInterceptor] pre-protocol [MessageModeOutInterceptorInternal, SAAJOutInterceptor, SOAPHandlerInterceptor, OurWSS4JOutInterceptor] write [SoapOutInterceptor] pre-marshal [LogicalHandlerOutInterceptor, ValidatingOutInterceptor*, TransformOutInterceptor*, ValidatingOutInterceptor*] marshal [BareOutInterceptor] write-ending [SoapOutEndingInterceptor] pre-protocol-ending [SAAJOutEndingInterceptor] pre-stream-ending [StaxOutEndingInterceptor] prepare-send-ending [MessageSenderEndingInterceptor] The interceptors marked with * are mine: - XmlDeclOutInterceptor forces the XML declaration to be written. - ValidatingOutInterceptor validates XML - TransformOutInterceptor transforms XML - ValidatingOutInterceptor validates XML The problem occurs if XML validation fails (the first validation in this test). When the XML validation fails, an exception thrown, caught, and re-thrown as a fault. At the start of fault processing, the chain when SAAJOutInterceptor is called looks like this: Chain org.apache.cxf.phase.phaseinterceptorch...@77c16c5f. Current flow: setup [ServerPolicyOutFaultInterceptor] prepare-send [MessageSenderInterceptor, Soap11FaultOutInterceptor] pre-stream [LoggingOutInterceptor, XmlDeclOutInterceptor*, StaxOutInterceptor] pre-protocol [WebFaultOutInterceptor, SAAJOutInterceptor, SOAPHandlerFaultOutInterceptor] write [SoapOutInterceptor] pre-marshal [LogicalHandlerFaultOutInterceptor] marshal [Soap11FaultOutInterceptorInternal] pre-protocol-ending [TransformOutFaultInterceptor*] prepare-send-ending [MessageSenderEndingInterceptor] The interceptors marked with * are mine: - XmlDeclOutInterceptor forces the XML declaration to be written. - TransformOutFaultInterceptor validates XML When I step through this SAAJOutInterceptor invocation, the saaj variable is NOT null, so the code path taken is different from what I described above. Instead of a W3CDOMStreamWriter, a dummy XMLStreamWriter is created that throws away whatever is written to it: //as the SOAPMessage already has everything in place, we do not need XMLStreamWriter to write //anything for us, so we just set XMLStreamWriter's output to a dummy output stream. XMLStreamWriter origWriter = message.getContent(XMLStreamWriter.class); message.put(ORIGINAL_XML_WRITER, origWriter); XMLStreamWriter dummyWriter = StaxUtils.createXMLStreamWriter(new OutputStream() { public void write(int b) throws IOException { } public void write(byte b[], int off, int len) throws IOException { } }); message.setContent(XMLStreamWriter.class, dummyWriter); No wonder I get no SOAP fault information back, I get: <?xml version='1.0' encoding='ISO-8859-1'?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <ais:requestID xmlns:ais="http://com.seagullsw.appinterface/AppInterfaceServer">{c0a80102-00ce16ad0000010e75da25398002}</ais:requestID> </SOAP-ENV:Header> <SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" /> </SOAP-ENV:Envelope> Is this a bug? Why would there be a dummy writer put in place? Is there another CXF way to do this? Thank you, Gary > -----Original Message----- > From: Gary Gregory > Sent: Friday, December 18, 2009 18:11 > To: 'Daniel Kulp'; users@cxf.apache.org > Subject: RE: Inflexible fault interceptor chain? > > Ok, that worked! > > Thank you Dan, > Gary > > > -----Original Message----- > > From: Daniel Kulp [mailto:dk...@apache.org] > > Sent: Friday, December 18, 2009 07:33 > > To: users@cxf.apache.org > > Cc: Gary Gregory; Lee Breisacher; Nikolay Glazyrin > > Subject: Re: Inflexible fault interceptor chain? > > > > > > Honestly, the EASIEST way to accomplish this, since you are using > soap, > > is to > > add the SAAJOutInterceptor to the fault chain. Then, your > interceptor > > would > > live right before it's "ending" interceptor and do: > > > > message.getContext(SOAPMessage.class) > > > > to get the SAAJ model out. Since the SAAJ model implements the DOM > > interfaces, you can then feed that into an XSLT processor or similar > to > > transform it and then set a new version back with the setContent > call. > > > > > > Dan > > > > > > On Fri December 18 2009 4:13:29 am Gary Gregory wrote: > > > Hi All: > > > > > > I need to apply an XSL transformation to messages coming out of CXF > > (our > > > users configure what the XSL looks like.) For a normal > (successful) > > > message, I have an interceptor (during Phase.PRE_MARSHAL) that > uses > > the > > > DOM aspect of a message. That works great. BTW, I get to the DOM > > like > > > this: > > > > > > Node node = (Node) message.getContent(List.class).get(0); > > > > > > That seems brittle, is there a safer way to get to an aspect of the > > message > > > I can feed to javax.xml.transform? > > > > > > The real issue comes with fault messages because the fault chain > uses > > an > > > > > > XMLStreamWriter<http://java.sun.com/javase/6/docs/api/javax/xml/stream/ > > XML > > > StreamWriter.html>. The fault chain looks like this: > > > > > > Chain org.apache.cxf.phase.phaseinterceptorch...@3015b303. Current > > flow: > > > setup [ServerPolicyOutFaultInterceptor] > > > prepare-send [MessageSenderInterceptor, > Soap11FaultOutInterceptor] > > > pre-stream [LoggingOutInterceptor, XmlDeclOutInterceptor*, > > > StaxOutInterceptor] pre-protocol [WebFaultOutInterceptor, > > > SOAPHandlerFaultOutInterceptor] write [SoapOutInterceptor] > > > pre-marshal [LogicalHandlerFaultOutInterceptor] > > > marshal [Soap11FaultOutInterceptorInternal] > > > pre-stream-ending [StaxOutEndingInterceptor, > > > TransformOutFaultInterceptor*] prepare-send-ending > > > [MessageSenderEndingInterceptor] > > > > > > FYI, the interceptors marked with * are our own: > > > > > > * XmlDeclOutInterceptor forces an XML declaration to be > > written. > > > > > > * TransformOutFaultInterceptor is where I thought I could > > transform > > > the fault XML message. > > > > > > The > > > > > > XMLStreamWriter<http://java.sun.com/javase/6/docs/api/javax/xml/stream/ > > XML > > > StreamWriter.html> looks like this: > > > > > > [StreamWriter: class com.ctc.wstx.sw.SimpleNsStreamWriter, > underlying > > > outputter: > > > > > > com.ctc.wstx.sw.isolatin1xmlwri...@1125cf44<mailto:com.ctc.wstx.sw.ISOL > > ati > > > n1xmlwri...@1125cf44> > > > > > > The com.ctc.wstx.sw.ISOLatin1XmlWriter wraps a > > > org.apache.cxf.io.CachedOutputStream, which in turns wraps: > > > > > > * currentStream - LoadingByteArrayOutputStream > > > > > > * flowThroughStream - > > AbstractHTTPDestination$WrappedOutputStream > > > > > > All of this to say that when the chain's interceptors are working > > with the > > > message's > > > > > > XMLStreamWriter<http://java.sun.com/javase/6/docs/api/javax/xml/stream/ > > XML > > > StreamWriter.html>, the bytes are cached and written to the wire. > It > > is not > > > possible to catch the fault XML message and change it. > > > > > > The only thing I've come up with but not implemented yet would be > to > > insert > > > an interceptor before the XML declaration is written and put the > > > > > > XMLStreamWriter<http://java.sun.com/javase/6/docs/api/javax/xml/stream/ > > XML > > > StreamWriter.html> into a temp spot in the message content map, > then > > put a > > > new > > > > > > XMLStreamWriter<http://java.sun.com/javase/6/docs/api/javax/xml/stream/ > > XML > > > StreamWriter.html> on a byte array in its place. A pre-stream- > ending > > > interceptor can take those bytes, apply XSL to them and then write > > them to > > > the original > > > > > > XMLStreamWriter<http://java.sun.com/javase/6/docs/api/javax/xml/stream/ > > XML > > > StreamWriter.html>, before putting the original > > > > > > XMLStreamWriter<http://java.sun.com/javase/6/docs/api/javax/xml/stream/ > > XML > > > StreamWriter.html> back in it original slot in the message content > > map. > > > > > > That seems like big old hack. > > > > > > Any ideas on a cleaner solution? > > > > > > Thank you, > > > Gary Gregory > > > Seagull Software > > > ggreg...@seagullsoftware.com > > > www.seagullsoftware.com > > > > > > > -- > > Daniel Kulp > > dk...@apache.org > > http://www.dankulp.com/blog