连接失败时如何使用 WSS4JOutInterceptor 在 CXF 中捕获 SOAP 请求消息

How to catch SOAP request message in CXF with WSS4JOutInterceptor when connection fails

我可以根据 this SO answer.

但是,如果服务器不可用,我会从 SAAJOutInterceptor(由 WSS4JOutInterceptor 添加)得到以下异常,并且不会调用 LoggingCallback,因此消息不是错误的:

org.apache.cxf.binding.soap.SoapFault: Connection refused: connect
        at org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor$SAAJOutEndingInterceptor.handleMessage(SAAJOutInterceptor.java:221)
        at org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor$SAAJOutEndingInterceptor.handleMessage(SAAJOutInterceptor.java:174)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
        at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:531)
        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:440)
        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:355)
        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:313)
        at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
        at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:140)
        ...
Caused by: com.ctc.wstx.exc.WstxIOException: Connection refused: connect
        at com.ctc.wstx.sw.BaseStreamWriter.flush(BaseStreamWriter.java:262)
        at org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor$SAAJOutEndingInterceptor.handleMessage(SAAJOutInterceptor.java:215)
        ...
Caused by: java.net.ConnectException: Connection refused: connect
        at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
        at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.net.Socket.connect(Socket.java:589)
        at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
        at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
        at sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
        at sun.net.www.http.HttpClient.New(HttpClient.java:339)
        at sun.net.www.http.HttpClient.New(HttpClient.java:357)
        at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1220)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1199)
        at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050)
        at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:984)
        at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1334)
        at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1309)
        at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.setupWrappedStream(URLConnectionHTTPConduit.java:274)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleHeadersTrustCaching(HTTPConduit.java:1345)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.onFirstWrite(HTTPConduit.java:1306)
        at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.onFirstWrite(URLConnectionHTTPConduit.java:307)
        at org.apache.cxf.io.AbstractWrappedOutputStream.write(AbstractWrappedOutputStream.java:47)
        at org.apache.cxf.io.AbstractThresholdOutputStream.unBuffer(AbstractThresholdOutputStream.java:89)
        at org.apache.cxf.io.AbstractThresholdOutputStream.write(AbstractThresholdOutputStream.java:63)
        at org.apache.cxf.io.CacheAndWriteOutputStream.write(CacheAndWriteOutputStream.java:80)
        at org.apache.cxf.io.CacheAndWriteOutputStream.write(CacheAndWriteOutputStream.java:80)
        at org.apache.cxf.io.AbstractWrappedOutputStream.write(AbstractWrappedOutputStream.java:51)
        at com.ctc.wstx.io.UTF8Writer.flush(UTF8Writer.java:100)
        at com.ctc.wstx.sw.BufferingXmlWriter.flush(BufferingXmlWriter.java:242)
        at com.ctc.wstx.sw.BaseStreamWriter.flush(BaseStreamWriter.java:260)
        ...

我需要保存请求 SOAP 消息以及 WSS4JOutInterceptor 添加的所有安全内容(证书、签名...),无论消息是否已成功发送或服务器是否已启动。

问题是您必须首先写入捕获消息的输出流,然后仅在 onClose 回调中写入网络连接:

class RequestInterceptor extends AbstractPhaseInterceptor<Message> {

    OutputStream outputStream
    OutputStream originalOutputStream

    public RequestInterceptor() {
        super(Phase.PRE_STREAM);
    }

    @Override
    public void handleMessage(Message message) throws Fault {
        originalOutputStream = message.getContent(OutputStream.class)
        CacheAndWriteOutputStream newOutputStream = new CacheAndWriteOutputStream(outputStream)
        message.setContent(OutputStream.class, newOutputStream)
        newOutputStream.registerCallback(new CachedOutputStreamCallback() {
            void onFlush(CachedOutputStream cos) {
            }
            void onClose(CachedOutputStream cos) {
                cos.writeCacheTo(originalOutputStream)
                originalOutputStream.close()
            }
        })
    }

}