How to get incoming & outgoing soap xml in a simple way using Apache CXF?

后端 未结 4 913
走了就别回头了
走了就别回头了 2020-12-03 00:24

I have been fiddling around with server side interceptors on CXF. But is seems that it is not a trivial task to implement simple incoming and outgoing interceptors that give

相关标签:
4条回答
  • 2020-12-03 00:35

    I just want to share one more option, how to get incoming and outgoing messages together at the same time for some logging purpose, for example log requests and corresponding responses to database.

    import javax.xml.namespace.QName;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.soap.SOAPPart;
    import javax.xml.transform.TransformerException;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.dom.DOMSource;
    import javax.xml.transform.stream.StreamResult;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
    import java.io.StringWriter;
    import java.util.Collections;
    import java.util.Set;
    
    public class CxfLoggingHandler implements SOAPHandler<SOAPMessageContext> {
    
    private static final String SOAP_REQUEST_MSG_KEY = "REQ_MSG";
    
    public Set<QName> getHeaders() {
        return Collections.EMPTY_SET;
    }
    
    public boolean handleMessage(SOAPMessageContext context) {
        Boolean outgoingMessage = (Boolean) context.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (outgoingMessage) {
            // it is outgoing message. let's work
            SOAPPart request = (SOAPPart)context.get(SOAP_REQUEST_MSG_KEY);
            String requestString = convertDomToString(request);
            String responseString = convertDomToString(context.getMessage().getSOAPPart());
            String soapActionURI = ((QName)context.get(MessageContext.WSDL_OPERATION)).getLocalPart();
            // now you can output your request, response, and ws-operation    
        } else {
            // it is incoming message, saving it for future
            context.put(SOAP_REQUEST_MSG_KEY, context.getMessage().getSOAPPart());
        }
        return true;
    }
    
    public boolean handleFault(SOAPMessageContext context) {        
        return handleMessage(context);
    }
    
    private String convertDomToString(SOAPPart soap){
        final StringWriter sw = new StringWriter();
        try {
            TransformerFactory.newInstance().newTransformer().transform(
                    new DOMSource(soap),
                    new StreamResult(sw));
        } catch (TransformerException e) {
            // do something
        }
        return sw.toString();
    }
    }
    

    and then connect that handler with webservice

    <jaxws:endpoint id="wsEndpoint" implementor="#myWS" address="/myWS" >
        <jaxws:handlers>
            <bean class="com.package.handlers.CxfLoggingHandler"/>
        </jaxws:handlers>
    </jaxws:endpoint>
    
    0 讨论(0)
  • 2020-12-03 00:38

    Found the code for an incoming interceptor here: Logging request/response with Apache CXF as XML

    My outgoing interceptor:

    import java.io.OutputStream;
    
    import org.apache.cxf.interceptor.Fault;
    import org.apache.cxf.interceptor.LoggingOutInterceptor;
    import org.apache.cxf.io.CacheAndWriteOutputStream;
    import org.apache.cxf.io.CachedOutputStream;
    import org.apache.cxf.io.CachedOutputStreamCallback;
    import org.apache.cxf.message.Message;
    import org.apache.cxf.phase.Phase;
    
    public class MyLogInterceptor extends LoggingOutInterceptor {
    
        public MyLogInterceptor() {
            super(Phase.PRE_STREAM);
        }
    
        @Override
        public void handleMessage(Message message) throws Fault {
            OutputStream out = message.getContent(OutputStream.class);
            final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(out);
            message.setContent(OutputStream.class, newOut);
            newOut.registerCallback(new LoggingCallback());
        }
    
        public class LoggingCallback implements CachedOutputStreamCallback {
            public void onFlush(CachedOutputStream cos) {
            }
    
            public void onClose(CachedOutputStream cos) {
                try {
                    StringBuilder builder = new StringBuilder();
                    cos.writeCacheTo(builder, limit);
                    // here comes my xml:
                    String soapXml = builder.toString();
                } catch (Exception e) {
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-03 00:50

    I could not get the above solution to work for me. This is what I developed and hope it can help others:

    My "incoming" interceptor:

    import org.apache.cxf.interceptor.LoggingInInterceptor;
    import org.apache.cxf.interceptor.LoggingMessage;
    
    public class MyCxfSoapInInterceptor extends LoggingInInterceptor {
    
    
        public MyCxfSoapInInterceptor() {
            super();
        }
    
        @Override
        protected String formatLoggingMessage(LoggingMessage loggingMessage) {
            String soapXmlPayload = loggingMessage.getPayload() != null ? loggingMessage.getPayload().toString() : null;
    
            // do what you want with the payload... in my case, I stuck it in a JMS Queue
    
            return super.formatLoggingMessage(loggingMessage);
        }
    }
    

    My "outgoing" interceptor:

    import org.apache.cxf.interceptor.LoggingMessage;
    import org.apache.cxf.interceptor.LoggingOutInterceptor;
    
    public class MyCxfSoapOutInterceptor extends LoggingOutInterceptor {
    
        public MyCxfSoapOutInterceptor() {
            super();
        }
    
        @Override
        protected String formatLoggingMessage(LoggingMessage loggingMessage) {
            String soapXmlPayload = loggingMessage.getPayload() != null ? loggingMessage.getPayload().toString() : null;
    
            // do what you want with the payload... in my case, I stuck it in a JMS Queue
    
            return super.formatLoggingMessage(loggingMessage);
        }
    }
    

    Something I added to my spring framework application context XML (remember to define the two interceptors in the XML file too)...

        ...
    
        <cxf:bus>
            <cxf:inInterceptors>
                <ref bean="myCxfSoapInInterceptor"/>
            </cxf:inInterceptors>
            <cxf:inFaultInterceptors>
                <ref bean="myCxfSoapInInterceptor"/>
            </cxf:inFaultInterceptors>
            <cxf:outInterceptors>
                <ref bean="myCxfSoapOutInterceptor"/>
            </cxf:outInterceptors>
            <cxf:outFaultInterceptors>
                <ref bean="myCxfSoapOutInterceptor"/>
            </cxf:outFaultInterceptors>
        </cxf:bus>
    
        ...
    

    Note, there are other ways to add the interceptors such as via annotations which will allow you to only intercept specific soap services. The above way of adding interceptors the the "bus" would intercept all your soap services.

    0 讨论(0)
  • 2020-12-03 00:52

    Example for writing the text to a StringBuffer, with hooks for capturing some custom properties and filtering of the request XML:

    public class XMLLoggingInInterceptor extends AbstractPhaseInterceptor<Message> {
    
        private static final String LOCAL_NAME = "MessageID";
    
        private static final int PROPERTIES_SIZE = 128;
    
        private String name = "<interceptor name not set>";
    
        protected PrettyPrinter prettyPrinter = null;
        protected Logger logger;
        protected Level reformatSuccessLevel;
        protected Level reformatFailureLevel;
    
        public XMLLoggingInInterceptor() {
            this(LogUtils.getLogger(XMLLoggingInInterceptor.class), Level.INFO,  Level.WARNING);
        }
    
        public XMLLoggingInInterceptor(PrettyPrinter prettyPrinter) {
            this(LogUtils.getLogger(XMLLoggingInInterceptor.class), Level.INFO,  Level.WARNING);
    
            this.prettyPrinter = prettyPrinter;
        }
    
        public XMLLoggingInInterceptor(Logger logger, Level reformatSuccessLevel, Level reformatFailureLevel) {
            super(Phase.RECEIVE);
            this.logger = logger;
            this.reformatSuccessLevel = reformatSuccessLevel;
            this.reformatFailureLevel = reformatFailureLevel;
        }
    
        public XMLLoggingInInterceptor(PrettyPrinter prettyPrinter, Logger logger, Level reformatSuccessLevel, Level reformatFailureLevel) {
            this(logger, reformatSuccessLevel, reformatFailureLevel);
            this.prettyPrinter = prettyPrinter;
            this.logger = logger;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void handleMessage(Message message) throws Fault {
    
            if (!logger.isLoggable(reformatSuccessLevel)) {
                 return;
            }
    
            InputStream in = message.getContent(InputStream.class);
            if (in == null) {
                return;
            }
    
            StringBuilder buffer;
    
            CachedOutputStream cache = new CachedOutputStream();
            try {
                InputStream origIn = in;
                IOUtils.copy(in, cache);
    
                if (cache.size() > 0) {
                    in = cache.getInputStream();
                } else {
                    in = new ByteArrayInputStream(new byte[0]);
                }
    
                // set the inputstream back as message payload
                message.setContent(InputStream.class, in);
    
                cache.close();
                origIn.close();
    
                int contentSize = (int) cache.size();
    
                buffer = new StringBuilder(contentSize + PROPERTIES_SIZE);
    
                cache.writeCacheTo(buffer, "UTF-8");
            } catch (IOException e) {
                throw new Fault(e);
            }
    
            // decode chars from bytes
            char[] chars = new char[buffer.length()];
            buffer.getChars(0, chars.length, chars, 0);
    
            // reuse buffer
            buffer.setLength(0);
    
            // perform local logging - to the buffer 
            buffer.append(name);
    
            logProperties(buffer, message);
    
            // pretty print XML
            if(prettyPrinter.process(chars, 0, chars.length, buffer)) {
                // log as normal
                logger.log(reformatSuccessLevel, buffer.toString());
            } else {
                // something unexpected - log as exception
                buffer.append(" was unable to format XML:\n");
                buffer.append(chars); // unmodified XML
    
                logger.log(reformatFailureLevel, buffer.toString());
            }
        }
    
    
        /**
         * Gets theMessageID header in the list of headers.
         *
         */
        protected String getIdHeader(Message message) {
            return getHeader(message, LOCAL_NAME);
        }
    
        protected String getHeader(Message message, String name) {
            List<Header> headers = (List<Header>) message.get(Header.HEADER_LIST);
    
            if(headers != null) {
                for(Header header:headers) {
                    if(header.getName().getLocalPart().equalsIgnoreCase(name)) {
                        return header.getObject().toString();
                    }
                }
            }
            return null;
        }        
    
        /**
         * Method intended for use within subclasses. Log custom field here.
         * 
         * @param message message
        */
    
        protected void logProperties(StringBuilder buffer, Message message) {
            final String messageId = getIdHeader(message);
            if(messageId != null) {
                buffer.append(" MessageId=");
                buffer.append(messageId);
            }
        }
    
        public void setPrettyPrinter(PrettyPrinter prettyPrinter) {
            this.prettyPrinter = prettyPrinter;
        }
    
        public PrettyPrinter getPrettyPrinter() {
            return prettyPrinter;
        }
    
        public Logger getLogger() {
            return logger;
        }
    
        public String getName() {
            return name;
        }
    
        public Level getReformatFailureLevel() {
            return reformatFailureLevel;
        }
    
        public Level getReformatSuccessLevel() {
            return reformatSuccessLevel;
        }
    
        public void setReformatFailureLevel(Level reformatFailureLevel) {
            this.reformatFailureLevel = reformatFailureLevel;
        }
    
        public void setReformatSuccessLevel(Level reformatSuccessLevel) {
            this.reformatSuccessLevel = reformatSuccessLevel;
        }
    
        public void setLogger(Logger logger) {
            this.logger = logger;
        }
    }
    

    For a fully working example, with output interceptors, see my CXF module on github.

    0 讨论(0)
提交回复
热议问题