2017-02-22 120 views
0

我是SOA開發上下文中CXF的用戶。CXF:如何重新發送來自CXF客戶端的原始請求?

我想知道如果我的問題有CXF解決方案。這是我的需要。 我們開發了一個服務於JAXWS端點的webapp,端點實現包括通過攔截器分析請求,將請求中的數據從Java服務層中的數據存儲到數據庫,並通過CXF客戶端將原始請求重新發送到另一臺服務器。 問題是我們的一些請求包含DSIG簽名(https://www.w3.org/TR/xmldsig-core/)或簽名的SAML聲明。 我們的需求是重新發送請求而不改變它們(例如代理)來自CXFClient。 CXF使用發送編組對象到服務器,但這樣原始流不發送

有沒有辦法重新從服務層從Java CXFClient傳入的請求而不改變它(簽名取決於請求格式:空格,命名空間前綴,回車...)?我們更喜歡CXFClient,因爲我們想重新使用我們自己的CXF攔截器來記錄傳出的請求。

我們已經測試了一個攔截器,以便在將原始請求發送到服務器之前替換outputStream,我們使用了這個答案:How To Modify The Raw XML message of an Outbound CXF Request?,但是我們仍然是KO,CXF仍然發送從編組對象產生的流。見下面的代碼。

上下文: - CXF 2.7.18(JDK 6)和3.1.10(JDK 8) - 平臺:視窗7的64位/ RHEL 7 64位 - 的Apache Tomcat 7 - TCPDUMP分析傳入流量

樣品我們的客戶代碼:

final Client cxfClient = org.apache.cxf.frontend.ClientProxy.getClient(portType); 
    cxfClient.getInInterceptors().clear(); 
cxfClient.getOutInterceptors().clear(); 
cxfClient.getOutFaultInterceptors().clear(); 
cxfClient.getRequestContext().put(CustomStreamerInterceptor.STREAM_TO_SEND, 
PhaseInterceptorChain.getCurrentMessage().getContent(InputStream.class)); 
cxfClient.getOutInterceptors().add(new CustomStreamerInterceptor()); 
org.apache.cxf.transport.http.HTTPConduit http = (org.apache.cxf.transport.http.HTTPConduit) cxfClient.getConduit(); 
... 
port.doSomething(someRequest); 

CustomStreamerInterceptor:

package test; 
import java.io.InputStream; 
import java.io.OutputStream; 
import org.apache.commons.io.IOUtils; 
import org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor.SoapOutEndingInterceptor; 
import org.apache.cxf.helpers.LoadingByteArrayOutputStream; 
import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor; 
import org.apache.cxf.interceptor.Fault; 
import org.apache.cxf.io.CacheAndWriteOutputStream; 
import org.apache.cxf.message.Message; 
import org.apache.cxf.phase.Phase; 
public class CustomStreamerInterceptor extends AbstractOutDatabindingInterceptor { 
     public static final String STREAM_TO_SEND = "STREAM_TO_SEND"; 
     public CustomStreamerInterceptor() { 
      super(Phase.WRITE_ENDING); 
      addAfter(SoapOutEndingInterceptor.class.getName()); 
     } 
     @Override 
     public void handleMessage(Message message) throws Fault { 
      try { 
        InputStream toSend = (InputStream) message.get(STREAM_TO_SEND); 
        if (toSend != null) { 
          toSend.reset(); 
          LoadingByteArrayOutputStream lBos = new LoadingByteArrayOutputStream(); 
          IOUtils.copy(toSend, lBos); 
          CacheAndWriteOutputStream cawos = (CacheAndWriteOutputStream) message.getContent(OutputStream.class); 
          cawos.resetOut(lBos, false);//fail ! 
        } 
      } 
      catch (Exception e) { 
        throw new Fault(e); 
      } 
     } 
} 

感謝您的任何幫助,這將是非常有用的。

回答

0

我認爲最好創建一個「經典」的HTTP客戶端,因爲CXF不是爲這種情況而設計的,它更常見的用它來將對象從java編組到XML ... 幸運的是,我處理這個問題與一個攔截器。您可以編寫一個攔截器,將CXF準備發送到服務器的輸出流對象中的流複製到該服務器。因爲如果使用Logging攔截器,您可能需要記錄傳出流,因此您需要謹慎對待攔截器的階段和順序。這個攔截器可以完成這項工作,確保它在任何日誌攔截器之後運行。代碼CXF 2.7.18:

import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import org.apache.commons.lang.CharEncoding; 
import org.apache.cxf.helpers.IOUtils; 
import org.apache.cxf.interceptor.Fault; 
import org.apache.cxf.interceptor.StaxOutInterceptor; 
import org.apache.cxf.message.Exchange; 
import org.apache.cxf.message.Message; 
import org.apache.cxf.phase.AbstractPhaseInterceptor; 
import org.apache.cxf.phase.Phase; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 

public class PassePlatClientInterceptorOut extends AbstractPhaseInterceptor<Message> { 
private static final Logger LOG = LoggerFactory.getLogger(PassePlatClientInterceptorOut.class); 

private final Exchange exchangeToReadFrom; 

public PassePlatClientInterceptorOut(final Exchange exchange) { 
    super(Phase.PRE_STREAM); 
    addBefore(StaxOutInterceptor.class.getName()); 
    this.exchangeToReadFrom = exchange; 
} 

@Override 
public void handleMessage(Message message) { 
    InputStream is = (InputStream) exchangeToReadFrom.get(PassePlatServerInterceptorIn.PASSE_PLAT_INTERCEPTOR_STREAM_SERVEUR); 
    if (is != null) { 
     message.put(org.apache.cxf.message.Message.ENCODING, CharEncoding.UTF_8); 
     OutputStream os = message.getContent(OutputStream.class); 
     try { 
      IOUtils.copy(is, os); 
      is.close(); 
     } 
     catch (IOException e) { 
      LOG.error("Error ...", e); 
      message.setContent(Exception.class, e); 
      throw new Fault(new Exception("Error ...", e)); 
     } 
     boolean everythingOK = message.getInterceptorChain().doInterceptStartingAt(message, 
       org.apache.cxf.interceptor.MessageSenderInterceptor.MessageSenderEndingInterceptor.class.getName()); 
     if (!everythingOK) { 
      LOG.error("Error ?"); 
      throw new Fault(new Exception("Error ...")); 
     } 
    } 
} 

}

要創建攔截器:

cxfClient.getInInterceptors().add(new PassePlatClientInterceptorIn(exchange));