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

Reply via email to