如何获得传入和使用Apache CXF以简单的方式传出soap xml?

时间:2012-06-14 17:29:22

标签: logging soap cxf interceptor

我一直在使用CXF上的服务器端拦截器。但似乎实现简单的传入和传出拦截器并不是一项简单的任务,这些拦截器为我提供了包含SOAP XML的纯字符串。

我需要在拦截器中使用纯XML,以便我可以将它们用于特定的日志记录任务。标准LogIn& LogOut拦截器不能胜任任务。是否有人愿意分享一些关于如何实现一个简单的传入拦截器的例子,它能够获取传入的SOAP XML和一个传出的拦截器来再次获取SOAP XML?

4 个答案:

答案 0 :(得分:16)

在这里找到传入拦截器的代码: Logging request/response with Apache CXF as XML

我的传出拦截器:

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) {
            }
        }
    }
}

答案 1 :(得分:5)

我无法让上述解决方案为我工作。这就是我的发展,希望它可以帮助他人:

我的“传入”拦截器:

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);
    }
}

我的“外向”拦截器:

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);
    }
}

我添加到spring框架应用程序上下文XML中的东西(记得在XML文件中定义两个拦截器)......

    ...

    <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>

    ...

请注意,还有其他方法可以添加拦截器,例如通过注释,这样您只能拦截特定的soap服务。上面添加拦截器的方法是“公共汽车”将拦截你所有的肥皂服务。

答案 2 :(得分:5)

我只想再分享一个选项,如何同时将传入和传出的消息放在一起以进行某些日志记录,例如日志请求和对数据库的相应响应。

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();
}
}

然后将该处理程序与webservice连接

<jaxws:endpoint id="wsEndpoint" implementor="#myWS" address="/myWS" >
    <jaxws:handlers>
        <bean class="com.package.handlers.CxfLoggingHandler"/>
    </jaxws:handlers>
</jaxws:endpoint>

答案 3 :(得分:3)

将文本写入StringBuffer的示例,其中包含用于捕获某些自定义属性和过滤请求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;
    }
}

有关输出拦截器的完整工作示例,请参阅github上的CXF module