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'; [email protected]
> Subject: RE: Inflexible fault interceptor chain?
>
> Ok, that worked!
>
> Thank you Dan,
> Gary
>
> > -----Original Message-----
> > From: Daniel Kulp [mailto:[email protected]]
> > Sent: Friday, December 18, 2009 07:33
> > To: [email protected]
> > 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
> > > [email protected]
> > > www.seagullsoftware.com
> > >
> >
> > --
> > Daniel Kulp
> > [email protected]
> > http://www.dankulp.com/blog