web-dev-qa-db-ja.com

Apache CXFを使用して単純な方法で着信および発信のSOAP XMLを取得する方法?

CXFでサーバー側のインターセプターをいじっています。しかし、SOAP XMLを含むプレーンな文字列を提供する単純な着信および発信インターセプターを実装することは簡単な仕事ではないようです。

特定のロギングタスクに使用できるように、インターセプターにプレーンXMLが必要です。標準のログインおよびログアウトインターセプターは、タスクに対応していません。着信SOAP XMLを取得できる単純な着信インターセプターと、再度SOAP XMLを取得できる発信インターセプターを実装する方法について、誰かが喜んで例を挙げてくれませんか?

15
Marco

着信インターセプターのコードをここに見つけました: Apache CXFを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) {
            }
        }
    }
}
17
annkatrin

上記の解決策がうまく機能しませんでした。これは私が開発したものであり、他の人に役立つことを願っています:

私の「着信」インターセプター:

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 FrameworkのアプリケーションコンテキストXMLに追加したもの(XMLファイルで2つのインターセプターも定義することを忘れないでください)...

    ...

    <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サービスのみをインターセプトできるようにするアノテーションなどを使用してインターセプターを追加する方法は他にもあります。上記の方法で「バス」にインターセプターを追加すると、すべてのSOAPサービスがインターセプトされます。

5
dulon

もう1つのオプションを共有したいと思います。たとえば、ログ要求やデータベースへの対応する応答など、ログ目的で着信メッセージと発信メッセージを同時に取得する方法です。

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

次に、そのハンドラをWebサービスに接続します

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

いくつかのカスタムプロパティをキャプチャしてリクエストXMLをフィルタリングするためのフックを使用して、テキストをStringBufferに書き込む例:

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のmy CXF module を参照してください。

3
ThomasRS