How To Modify The Raw XML message of an Outbound CXF Request?

前端 未结 6 552
滥情空心
滥情空心 2020-11-27 17:07

I would like to modify an outgoing SOAP Request. I would like to remove 2 xml nodes from the Envelope\'s body. I managed to set up an Interceptor and get the generated Stri

相关标签:
6条回答
  • 2020-11-27 17:39

    Following is able to bubble up server side exceptions. Use of os.close() instead of IOUtils.closeQuietly(os) in previous solution is also able to bubble up exceptions.

    public class OutInterceptor extends AbstractPhaseInterceptor<Message> {     
        public OutInterceptor() { 
            super(Phase.PRE_STREAM); 
            addBefore(StaxOutInterceptor.class.getName()); 
        }   
        public void handleMessage(Message message) { 
            OutputStream os = message.getContent(OutputStream.class); 
            CachedOutputStream cos = new CachedOutputStream(); 
            message.setContent(OutputStream.class, cos); 
            message.getInterceptorChain.aad(new PDWSOutMessageChangingInterceptor(os)); 
        }
    } 
    
    public class OutMessageChangingInterceptor extends AbstractPhaseInterceptor<Message> {
        private OutputStream os; 
    
        public OutMessageChangingInterceptor(OutputStream os){
            super(Phase.PRE_STREAM_ENDING); 
            addAfter(StaxOutEndingInterceptor.class.getName()); 
            this.os = os;
        } 
    
        public void handleMessage(Message message) { 
            try { 
                CachedOutputStream csnew = (CachedOutputStream) message .getContent(OutputStream.class);
                String currentEnvelopeMessage = IOUtils.toString( csnew.getInputStream(), (String) message.get(Message.ENCODING)); 
                csnew.flush(); 
                IOUtils.closeQuietly(csnew); 
                String res = changeOutboundMessage(currentEnvelopeMessage); 
                res = res != null ? res : currentEnvelopeMessage; 
                InputStream replaceInStream = IOUtils.tolnputStream(res, (String) message.get(Message.ENCODING)); 
                IOUtils.copy(replaceInStream, os); 
                replaceInStream.close(); 
                IOUtils.closeQuietly(replaceInStream);
                message.setContent(OutputStream.class, os);
            } catch (IOException ioe) {
                throw new RuntimeException(ioe);  
            }
        }
    } 
    
    0 讨论(0)
  • 2020-11-27 17:44

    I had this problem as well today. After much weeping and gnashing of teeth, I was able to alter the StreamInterceptor class in the configuration_interceptor demo that comes with the CXF source:

    OutputStream os = message.getContent(OutputStream.class);
    CachedStream cs = new CachedStream();
    message.setContent(OutputStream.class, cs);
    
    message.getInterceptorChain().doIntercept(message);
    
    try {
        cs.flush();
        CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
    
        String soapMessage = IOUtils.toString(csnew.getInputStream());
        ...
    

    The soapMessage variable will contain the complete SOAP message. You should be able to manipulate the soap message, flush it to an output stream and do a message.setContent(OutputStream.class... call to put your modifications on the message. This comes with no warranty, since I'm pretty new to CXF myself!

    Note: CachedStream is a private class in the StreamInterceptor class. Don't forget to configure your interceptor to run in the PRE_STREAM phase so that the SOAP interceptors have a chance to write the SOAP message.

    0 讨论(0)
  • 2020-11-27 17:47

    a better way would be to modify the message using the DOM interface, you need to add the SAAJOutInterceptor first (this might have a performance hit for big requests) and then your custom interceptor that is executed in phase USER_PROTOCOL

    import org.apache.cxf.binding.soap.SoapMessage;
    import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.phase.Phase;
    import org.w3c.dom.Node;
    
    import javax.xml.soap.SOAPException;
    import javax.xml.soap.SOAPMessage;
    
    abstract public class SoapNodeModifierInterceptor extends AbstractSoapInterceptor {
        SoapNodeModifierInterceptor() { super(Phase.USER_PROTOCOL); }
    
        @Override public void handleMessage(SoapMessage message) throws Fault {
            try {
                if (message == null) {
                    return;
                }
                SOAPMessage sm = message.getContent(SOAPMessage.class);
                if (sm == null) {
                    throw new RuntimeException("You must add the SAAJOutInterceptor to the chain");
                }
    
                modifyNodes(sm.getSOAPBody());
    
            } catch (SOAPException e) {
                throw new RuntimeException(e);
            }
        }
    
        abstract void modifyNodes(Node node);
    }
    
    0 讨论(0)
  • 2020-11-27 17:48

    Good example for replacing outbound soap content based on this

    package kz.bee.bip;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import org.apache.commons.io.IOUtils;
    import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
    import org.apache.cxf.io.CachedOutputStream;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    
    public class SOAPOutboundInterceptor extends AbstractPhaseInterceptor<Message> {
    
        public SOAPOutboundInterceptor() {
            super(Phase.PRE_STREAM);
            addBefore(SoapPreProtocolOutInterceptor.class.getName());
        }
    
        public void handleMessage(Message message) {
    
            boolean isOutbound = false;
            isOutbound = message == message.getExchange().getOutMessage()
                    || message == message.getExchange().getOutFaultMessage();
    
            if (isOutbound) {
                OutputStream os = message.getContent(OutputStream.class);
                CachedStream cs = new CachedStream();
                message.setContent(OutputStream.class, cs);
    
                message.getInterceptorChain().doIntercept(message);
    
                try {
                    cs.flush();
                    IOUtils.closeQuietly(cs);
                    CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
    
                    String currentEnvelopeMessage = IOUtils.toString(csnew.getInputStream(), "UTF-8");
                    csnew.flush();
                    IOUtils.closeQuietly(csnew);
    
                    /* here we can set new data instead of currentEnvelopeMessage*/
                    InputStream replaceInStream = IOUtils.toInputStream(currentEnvelopeMessage, "UTF-8");
    
                    IOUtils.copy(replaceInStream, os);
                    replaceInStream.close();
                    IOUtils.closeQuietly(replaceInStream);
    
                    os.flush();
                    message.setContent(OutputStream.class, os);
                    IOUtils.closeQuietly(os);
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
    
        public void handleFault(Message message) {
        }
    
        private static class CachedStream extends CachedOutputStream {
            public CachedStream() {
                super();
            }
    
            protected void doFlush() throws IOException {
                currentStream.flush();
            }
    
            protected void doClose() throws IOException {
            }
    
            protected void onWrite() throws IOException {
            }
        }
    }
    
    
    0 讨论(0)
  • 2020-11-27 17:53

    this one's working for me. It's based on StreamInterceptor class from configuration_interceptor example in Apache CXF samples.

    It's in Scala instead of Java but the conversion is straightforward.

    I tried to add comments to explain what's happening (as far as I understand).

    import java.io.OutputStream
    
    import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor
    import org.apache.cxf.helpers.IOUtils
    import org.apache.cxf.io.CachedOutputStream
    import org.apache.cxf.message.Message
    import org.apache.cxf.phase.AbstractPhaseInterceptor
    import org.apache.cxf.phase.Phase
    
    // java note: base constructor call is hidden at the end of class declaration
    class StreamInterceptor() extends AbstractPhaseInterceptor[Message](Phase.PRE_STREAM) {
    
      // java note: put this into the constructor after calling super(Phase.PRE_STREAM);
      addBefore(classOf[SoapPreProtocolOutInterceptor].getName)
    
      override def handleMessage(message: Message) = {
        // get original output stream
        val osOrig = message.getContent(classOf[OutputStream])
        // our output stream
        val osNew = new CachedOutputStream
        // replace it with ours
        message.setContent(classOf[OutputStream], osNew)
        // fills the osNew instead of osOrig
        message.getInterceptorChain.doIntercept(message)
        // flush before getting content
        osNew.flush()
        // get filled content
        val content = IOUtils.toString(osNew.getInputStream, "UTF-8")
        // we got the content, we may close our output stream now
        osNew.close()
        // modified content
        val modifiedContent = content.replace("a-string", "another-string")
        // fill original output stream
        osOrig.write(modifiedContent.getBytes("UTF-8"))
        // flush before set
        osOrig.flush()
        // replace with original output stream filled with our modified content
        message.setContent(classOf[OutputStream], osOrig)
      }
    }
    
    
    0 讨论(0)
  • 2020-11-27 17:56

    Based on the first comment, I created an abstract class which can easily be used to change the whole soap envelope.

    Just in case someone wants a ready-to-use code part.

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
    import org.apache.cxf.io.CachedOutputStream;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.AbstractPhaseInterceptor;
    import org.apache.cxf.phase.Phase;
    import org.apache.log4j.Logger;
    
    /**
     * http://www.mastertheboss.com/jboss-web-services/apache-cxf-interceptors
     * http://stackoverflow.com/questions/6915428/how-to-modify-the-raw-xml-message-of-an-outbound-cxf-request
     * 
     */
    public abstract class MessageChangeInterceptor extends AbstractPhaseInterceptor<Message> {
    
        public MessageChangeInterceptor() {
            super(Phase.PRE_STREAM);
            addBefore(SoapPreProtocolOutInterceptor.class.getName());
        }
    
        protected abstract Logger getLogger();
    
        protected abstract String changeOutboundMessage(String currentEnvelope);
    
        protected abstract String changeInboundMessage(String currentEnvelope);
    
        public void handleMessage(Message message) {
            boolean isOutbound = false;
            isOutbound = message == message.getExchange().getOutMessage()
                    || message == message.getExchange().getOutFaultMessage();
    
            if (isOutbound) {
                OutputStream os = message.getContent(OutputStream.class);
    
                CachedStream cs = new CachedStream();
                message.setContent(OutputStream.class, cs);
    
                message.getInterceptorChain().doIntercept(message);
    
                try {
                    cs.flush();
                    IOUtils.closeQuietly(cs);
                    CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
    
                    String currentEnvelopeMessage = IOUtils.toString(csnew.getInputStream(), "UTF-8");
                    csnew.flush();
                    IOUtils.closeQuietly(csnew);
    
                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("Outbound message: " + currentEnvelopeMessage);
                    }
    
                    String res = changeOutboundMessage(currentEnvelopeMessage);
                    if (res != null) {
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("Outbound message has been changed: " + res);
                        }
                    }
                    res = res != null ? res : currentEnvelopeMessage;
    
                    InputStream replaceInStream = IOUtils.toInputStream(res, "UTF-8");
    
                    IOUtils.copy(replaceInStream, os);
                    replaceInStream.close();
                    IOUtils.closeQuietly(replaceInStream);
    
                    os.flush();
                    message.setContent(OutputStream.class, os);
                    IOUtils.closeQuietly(os);
    
                } catch (IOException ioe) {
                    getLogger().warn("Unable to perform change.", ioe);
                    throw new RuntimeException(ioe);
                }
            } else {
                try {
                    InputStream is = message.getContent(InputStream.class);
                    String currentEnvelopeMessage = IOUtils.toString(is, "UTF-8");
                    IOUtils.closeQuietly(is);
    
                    if (getLogger().isDebugEnabled()) {
                        getLogger().debug("Inbound message: " + currentEnvelopeMessage);
                    }
    
                    String res = changeInboundMessage(currentEnvelopeMessage);
                    if (res != null) {
                        if (getLogger().isDebugEnabled()) {
                            getLogger().debug("Inbound message has been changed: " + res);
                        }
                    }
                    res = res != null ? res : currentEnvelopeMessage;
    
                    is = IOUtils.toInputStream(res, "UTF-8");
                    message.setContent(InputStream.class, is);
                    IOUtils.closeQuietly(is);
                } catch (IOException ioe) {
                    getLogger().warn("Unable to perform change.", ioe);
    
                    throw new RuntimeException(ioe);
                }
            }
        }
    
        public void handleFault(Message message) {
        }
    
        private class CachedStream extends CachedOutputStream {
            public CachedStream() {
                super();
            }
    
            protected void doFlush() throws IOException {
                currentStream.flush();
            }
    
            protected void doClose() throws IOException {
            }
    
            protected void onWrite() throws IOException {
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题