2013-07-09 32 views
0

我實現了使用優秀ws-lite library委派到多個重載方法在常規

不幸的是,庫不AFAIK附帶日誌支持針對合作伙伴皁後端一個小的客戶端,但我發現this博客,描述如何使用功能組合來委託。

現在我想爲原始SoapClient類中的所有類型的發送方法添加日誌記錄。我確信使用一些Groovy元編程黑魔法是可能的,但是我還沒有找到任何關於如何實現的例子,而且在動態元編程方面我仍然是一個小菜鳥。 我想要的是添加具有相同簽名的方法,在委託給原始方法之前調用日誌記錄和錯誤處理。

我也希望只有一個地方能夠讓我保持乾爽,並且在API發展時無需適應任何可能的未來重載版本。

的的SOAPClient具有發送方法,如:

public SOAPResponse send(java.util.Map requestParams, groovy.lang.Closure content) 
public SOAPResponse send(java.util.Map requestParams, java.lang.String content) 
public SOAPResponse send(java.util.Map requestParams, wslite.soap.SOAPVersion soapVersion, java.lang.String content) 

現在我可以擴展類,重載的方法,並用我的生命去。但我想知道Groovier(以及未來的證明)如何實現這一點。

+1

這是否工作:http://naleid.com/blog/2010/09/11/adding-logging-around-all-of-the-methods -of-a-class-with-groovy/ –

回答

0

我通過查看tim_yates提到的示例找到了解決方案。 我能找到的最清晰的方式是在groovy.runtime.metaclass.wslite.soap下添加一個MetaClass,以便按照指示here的指示自動MetaClass註冊。

該類然後包含invokeMethod,該委託給實際的肥皂客戶端。其實挺不錯,但有點過分巫術來定期使用(因爲大多數AOP編程恕我直言)

package groovy.runtime.metaclass.wslite.soap 

import groovy.transform.ToString 
import groovy.util.logging.Log4j 
import mycomp.soap.InfrastructureException 
import mycomp.soap.HttpLogger 
import wslite.http.HTTPClientException 
import wslite.soap.SOAPFaultException 
import wslite.soap.SOAPResponse 


/** 
* Extension to the wslite client for logging and error handling. 
* The package placement and class name is dictated by Groovy rules for Meta class loading (see http://groovy.codehaus.org/Using+the+Delegating+Meta+Class) 
* Method invocation on SOAPClient will be proxied by this class which adds convenience methods. 
* 
*/ 
class SOAPClientMetaClass extends DelegatingMetaClass{ 

    //Delegating logger in our package name, so log4j configuration does not need to know about this weird package 
    private final HttpLogger logger 

    SOAPClientMetaClass(MetaClass delegate){ 
     super(delegate) 
     logger = new HttpLogger() 
    } 

    @Override 
    Object invokeMethod(Object object, String methodName, Object[] args) { 
     if(methodName == "send"){ 
      withLogging { 
       withExceptionHandler { 
        return super.invokeMethod(object, "send", args) 
       } 
      } 
     } else { 
      return super.invokeMethod(object, methodName, args) 
     } 
    } 

    private SOAPResponse withLogging(Closure cl) { 
     SOAPResponse response = cl.call() 
     logger.log(response?.httpRequest, response?.httpResponse) 
     return response 
    } 


    private SOAPResponse withExceptionHandler(Closure cl) { 
     try { 
      return cl.call() 
     } catch (SOAPFaultException soapEx) { 
      logger.log(soapEx.httpRequest, soapEx.httpResponse) 
      def message = soapEx.hasFault() ? soapEx.fault.text() : soapEx.message 
      throw new InfrastructureException(message) 
     } catch (HTTPClientException httpEx) { 
      logger.log(httpEx.request, httpEx.response) 
      throw new InfrastructureException(httpEx.message) 
     } 
    } 
} 
+0

我認爲你可以在@tim_yates建議的博客之後得到你需要的東西,雖然創建一個'DelegatingMetaClass'也很好,在編譯時註冊元類。您只需在運行時使用metaClass'SOAPClient'即可。希望[這](http://stackoverflow.com/a/17438880/2051952)會有所幫助,這與建議的博客相同。你可以在你的答案中使用'invokeMethod'中的相同方法。另一方面,如果'DelegatingMetaClass'是編譯時(DRY),我不介意或將它視爲voodoo,並確保在運行時不是選項時需要什麼。 – dmahapatro

+0

是的,你的解決方案看起來像@tim_yates顯示的那樣。也就是說,添加方法調用作爲實例的一個屬性。在我的情況下,我喜歡讓MetaClass擴展我的應用程序中所有實例的庫。這就是爲什麼我選擇使用命名約定註冊它的原因。關於voodo評論,我只是表示很容易錯過使用這種猴子修補代碼風格(註冊表版本)實現的實際運行時邏輯,這顯然有其優點和缺點。但是,謝謝你的擡頭 – Billybong

0

剛擡起頭,任何人想重複使用此代碼。

這實際上是一個壞主意,因爲send方法被soapclient本身重載和委託。結果是元類捕獲了內部代表並記錄了三次或兩次(取決於我所稱的實際方法)。 一次爲我的呼叫,然後每次重載的方法稱爲其他方法。

我最終選擇了一個類似於博客中描述的更簡單的解決方案。這是用我自己的實際客戶端包裝:

@Log4j 
class WSClient { 

    @Delegate 
    final SOAPClient realClient 

    WSClient(SOAPClient realClient) { 
     this.realClient = realClient 
    } 

    SOAPResponse sendWithLog(args){ 
     withLogging{ 
      withExceptionHandler{ 
       realClient.send(args) 
      } 
     } 
    } 

    def withLogging = { cl -> 
     SOAPResponse response = cl() 
     logHttp(Level.DEBUG, response?.httpRequest, response?.httpResponse) 
     return response 
    } 


    def withExceptionHandler = {cl -> 
     try { 
      return cl() 
     } catch (SOAPFaultException soapEx) { 
      logHttp(Level.ERROR, soapEx.httpRequest, soapEx.httpResponse) 
      def message = soapEx.hasFault() ? soapEx.fault.text() : soapEx.message 
      throw new InfrastructureException(message) 
     } catch (HTTPClientException httpEx) { 
      logHttp(Level.ERROR, httpEx.request, httpEx.response) 
      throw new InfrastructureException(httpEx.message) 
     } 
    } 

    private void logHttp(Level priority, HTTPRequest request, HTTPResponse response) { 
     log.log(priority, "HTTPRequest $request with content:\n${request?.contentAsString}") 
     log.log(priority, "HTTPResponse $response with content:\n${response?.contentAsString}") 
    } 
}