0

優れたws-lite ライブラリを使用して、パートナーの SOAP バックエンドに対して小さなクライアントを実装しました

残念ながら、ライブラリにはログ記録のサポートが含まれていませんが、機能構成を使用して委任する方法について説明しているこのブログを見つけました。

ここで、元の SoapClient クラスにすべての種類の send メソッドのログを追加したいと思います。Groovy メタプログラミングの黒魔術でそれが可能であると確信していますが、その方法の例を見つけられず、動的メタプログラミングに関してはまだ初心者です。私が望むのは、元のメソッドに委譲する前に、ロギングとエラー処理を呼び出す同じシグネチャを持つメソッドを追加することです。

また、API が進化したときに将来的にオーバーロードされる可能性のあるバージョンに適応する必要がないように、これを 1 か所にまとめて DRY に保ちたいと考えています。

SOAPClient には、次のような send メソッドがあります。

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(および将来の証拠)の方法を知りたいです。

4

2 に答える 2

0

このコードの再利用を考えている人は注意してください。

send メソッドがオーバーロードされ、soapclient 自体によって委譲されるため、これは実際には悪い考えであることが判明しました。その結果、メタクラスは内部委譲をキャッチし、3 回または 2 回ログに記録しました (実際に呼び出したメソッドによって異なります)。自分の呼び出しで 1 回、次にオーバーロードされたメソッドが他のメソッドを呼び出すたびに。

私は最終的に、ブログで説明されているものに似た、より単純なソリューションに落ち着きました。それは、実際のクライアントを自分のものでラップすることです:

@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}")
    }
}
于 2013-07-15T11:22:25.637 に答える
0

tim_yates が参照した例を見て解決策を見つけました。私が見つけた最も巧妙な方法は、ここで説明されているように自動 MetaClass 登録のために groovy.runtime.metaclass.wslite.soap の下に MetaClass を追加することでした。

このクラスは、実際の SOAP クライアントに委譲する invokeMethod をオーバーライドします。実際にはかなりいいですが、定期的に使用するには少しブードゥー教が多すぎます(ほとんどのAOPプログラミングのIMHOとして)

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)
        }
    }
}
于 2013-07-09T13:14:39.903 に答える