Google App EngineにDWR2+SpringframeworkDI&AOPを乗せてみた

前回のログでは、Google App Engine(GAE)にSpringframeworkのDI(Dependency Injection)を乗せて、無事に動くことを見た。

今回は、Springframeworkの柱のもう一つである、AOPAspect Oriented Programing)を乗せてみるAOPでWeavingするので、前回のサンプルプログラムは殆ど変更せず、jarの追加、Advisorの追加、定義ファイルの変更のみ行う。

Weavingするのは、メソッドの実行の前後でLog4jでクラス名とメソッド名をロギングするAdvice。
aopアライアンス(aop aliance)を使った以下のコードがそれである(注記:このコードはmezawa氏作成のものを流用させてもらった。ライセンスはApache License2.0EzoGPプロジェクトに帰属するので注意願いたい)。これを、srcフォルダーのtestパッケージに作成する。

/* **********************************************************************
 * $Id: MethodLogMarker.java 776 2009-05-20 18:20:37Z mezawa_takuji $
 * 
 * Takuji Mezawa( project: Syracava_EGP )
 * 
 * (C) 2008, EzoGP.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ********************************************************************** */
package test;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public final class MethodLogMarker implements MethodInterceptor {
    private static final Log log = LogFactory.getLog(MethodLogMarker.class);
    
    public Object invoke(MethodInvocation invocation) throws Throwable {
        String invokedClassName = invocation.getThis().getClass().getName();
        String invokedMethodName = invocation.getMethod().getName();
        
        /* メソッド開始のログメッセージを出力 */
        log.info(markup(invokedClassName, invokedMethodName,true));
        
        try {
            /* ウィービングされるオブジェクトメソッドの処理を実行する */
            return invocation.proceed();
            
        } finally {
            /* メソッド終了のログメッセージを出力 */
            log.info(markup(invokedClassName, invokedMethodName, false));
        }
    }
    
    /*
     * インターセプトした処理の前後にINFOメッセージを出力するための編集処理。
     */
    private String markup(String className, String methodName, boolean isStart) {
        StringBuilder message = new StringBuilder();
        if (isStart) {
            message.append("STARTED ");
        } else {
            message.append("FINISHED ");
        }
        message.append(className).append(".").append(methodName);
        
        return message.toString();
    }
}

プロジェクトの作成と、jarのコピー

作業は、GAEプラグインがインストールされたEclipse3.4(Ganymede)で行った。
前回のログで作成したプロジェクト(GaeSpringFrameworkTest)をコピーして、新しいプロジェクトを作成する(GaeSpringFrameworkTest2)。

springダウンロードファイル(spring-framework-2.5.6.SEC01-with-dependencies.zip)に含まれる。

  • spring-aspects.jar
  • aopalliance.jar
  • aspectjrt.jar
  • aspectjweaver.jar
  • cglib-nodep-2.1_3.jar

をwar/WEB-INF/libにコピーし、ビルドパスに追加する。

beans.xmlの変更

war/WEB-INF/beans.xmlAOPの設定を追加する。具体的には、

  • aopのxsdの追加
  • aopのアドバイス、ポイントカットの定義の追加

を行う。ポイントカット(methodLogMarker)の定義では、testパッケージの全てのクラスの全てのメソッドの呼び出し、をWeavingの対象に設定した。

<?xml version="1.0" encoding="utf-8"?>
<beans default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	    http://www.springframework.org/schema/aop
    	http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    	http://www.springframework.org/schema/util
    	http://www.springframework.org/schema/util/spring-util-2.5.xsd">

    <bean id="demoBean" 
    		class="test.Demo">
    	<property name="helloMessages">
    		<ref bean="messages"/>
    	</property>
    </bean>
	
    <util:map id="messages" map-class="java.util.HashMap">
        <entry key="msg1" value="こんにちは! " />
        <entry key="msg2" value="こんばんわ! " />
    </util:map>
	
    <bean id="methodLog" class="test.MethodLogMarker" />
    <aop:config>
        <!-- Definition of Pointcut-->
        <aop:pointcut id="methodLogMarker"
            expression="execution(* test..*(..))" />
        
        <!-- Definition of Advisor -->
        <aop:advisor pointcut-ref="methodLogMarker" advice-ref="methodLog" />
    </aop:config>

</beans>

デプロイと実行

以上で、設定は終わりである。AOPのテストなので、Demo.javaといった既存のクラスを触ることはない。プロジェクトを選択し、「Google」->「Deploy to Web App Engine」でGAEにデプロイをする。以下が、これを実行した画面である。

以下が、GAEの管理コンソールでLogを参照したときの出力である。キチンとメソッドの開始と終了にロギングが埋め込まれている。

1点疑問は、ログを消すにはどうしたらいいのだろうということ。GAEではファイルシステムへのアクセスが制限されているので、ログも「お任せ」になっているが、AOPでこういうAdviceを滑り込ませると、相当のログを吐いてしまう。1回削除して再デプロイなのかなぁ。

(注記)
以前のログに書いたが、DWR2でscope=sessionと設定する際には注意が必要である。こう設定した場合、GAEは厳密にJavaBeanがシリアライズ可能であること(java.io.Serializableをimplしている)ことを求める。
上の例では、testパッケージの全てのメソッドにMethodLogMarker.javaでWeavingをかけているが、scope=sessionのJavaBeanにWeavingをかけると、MethodLogMarkerもSerializableをimplするように求めてくる