Webエンジニア susumuis の技術ブログ

このブログの内容は個人の見解であり、所属する組織の公式見解ではありません

MayaaでGuice2.0 AOPを使うとうまく動かない件

View層にMayaaを使っているアプリケーションで、AOP的な機能が必要になり、Guice 2.0 を試したところ、はまってしまいました。

MLに質問したところ、簡単に試せるサンプルが欲しいとのことなので、作りましたが、公開する場所がないので、とりあえずここに書きます。

環境

以下の環境でテストしました。

プロジェクト作成

プロジェクトのベースとして、http://mayaa.seasar.org/downloads/index.html から、"Blank War"を使用しました。
Guiceは、http://code.google.com/p/google-guice/downloads/list からguice-2.0.zip を取得し、guice-2.0.jar と、aopalliance.jarだけをlibの中にコピーしました。

ソースコード

これに以下のコードを追加・修正します。

まず、ビーン

package example;
import java.util.Date;

public class MyBean {
	private String name;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getHello() {
		return "hello," + getName() + ". it's" + new Date();
	}
}

ビーンにアスペクトを挿入するモジュール

package example;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.google.inject.AbstractModule;
import com.google.inject.matcher.Matchers;

class MyModule extends AbstractModule {

	private static class MyIntercepter implements MethodInterceptor {
		public Object invoke(MethodInvocation invocation) throws Throwable {
			System.out.println(invocation.getThis().getClass().getName() + "#" + invocation.getMethod().getName() + "start");
			try {
				return invocation.proceed();
			} finally {
				System.out.println(invocation.getThis().getClass().getName() + "#" + invocation.getMethod().getName() + "end");
			}
		}
	}

	@Override
	protected void configure() {
		bindInterceptor(
				Matchers.subclassesOf(MyBean.class),
				Matchers.any(),
				new MyIntercepter());
	}
}

ビーンを生成してリクエストに格納するサーブレット

package example;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class MyServlet extends HttpServlet{
	
	private static Injector injector = Guice.createInjector(new MyModule());
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		MyBean bean;
		if ("true".equals(req.getParameter("aop"))) {
			bean = injector.getInstance(MyBean.class);
		} else {
			bean = new MyBean();
		}
		
		bean.setName(req.getParameter("name"));
		req.setAttribute("bean", bean);
		req.getRequestDispatcher("/view.html").forward(req, resp);
	}
}

結果を表示するview.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
	<head>
		<meta http-equiv="content-type" content="text/html; charset=UTF-8">
		<title>Hello</title>
	</head>
	<body>
		message:<span id="hello">HELLO</span>
	</body>
</html>

view.mayaa

<?xml version="1.0" encoding="UTF-8"?>
<m:mayaa xmlns:m="http://mayaa.seasar.org">
	<m:write m:id="hello" value="${request.bean.getHello();}" />
</m:mayaa>

最後にweb.xml

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE web-app
	PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
	"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>

	<servlet>
		<servlet-name>MayaaServlet</servlet-name>
		<servlet-class>org.seasar.mayaa.impl.MayaaServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet>
		<servlet-name>MyServlet</servlet-name>
		<servlet-class>example.MyServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>MayaaServlet</servlet-name>
		<url-pattern>*.html</url-pattern>
	</servlet-mapping>
	
	<servlet-mapping>
		<servlet-name>MyServlet</servlet-name>
		<url-pattern>/myservlet</url-pattern>
	</servlet-mapping>

</web-app>

実行結果:

message:hello,hoge. it'sSun Sep 06 12:33:40 JST 2009

こちらは画面はTomcatエラーが表示され、以下のログメッセージが出力されていました。

example.MyBean$$EnhancerByGuice$$c8e25a70#setNamestart
example.MyBean$$EnhancerByGuice$$c8e25a70#setNameend
2009/09/06 12:34:25 org.seasar.mayaa.impl.engine.error.TemplateErrorHandler doErrorHandle
情報: error template not found, /java.lang.NullPointerException
2009/09/06 12:34:25 org.seasar.mayaa.impl.engine.error.TemplateErrorHandler doErrorHandle
情報: error template not found, /java.lang.RuntimeException
2009/09/06 12:34:25 org.seasar.mayaa.impl.engine.error.TemplateErrorHandler doErrorHandle
情報: error template not found, /java.lang.Exception
java.lang.NullPointerException
at org.mozilla.javascript.net.sf.retrotranslator.runtime.impl.MethodDescriptor.getInstance(MethodDescriptor.java:137)
at org.mozilla.javascript.net.sf.retrotranslator.runtime.java.lang.reflect._Constructor.isVarArgs(_Constructor.java:83)
at org.mozilla.javascript.jdk15.VMBridge_jdk15.isVarArgs(VMBridge_jdk15.java:66)
at org.mozilla.javascript.MemberBox.init(MemberBox.java:86)
at org.mozilla.javascript.MemberBox.(MemberBox.java:72)
at org.mozilla.javascript.JavaMembers.reflect(JavaMembers.java:667)
at org.mozilla.javascript.JavaMembers.(JavaMembers.java:76)
at org.mozilla.javascript.JavaMembers.lookupClass(JavaMembers.java:838)
at org.mozilla.javascript.NativeJavaObject.initMembers(NativeJavaObject.java:90)
at org.mozilla.javascript.NativeJavaObject.(NativeJavaObject.java:80)
at org.mozilla.javascript.NativeJavaObject.(NativeJavaObject.java:70)
at org.mozilla.javascript.WrapFactory.wrapAsJavaObject(WrapFactory.java:149)
at org.seasar.mayaa.impl.cycle.script.rhino.WrapFactoryImpl.wrapAsJavaObject(WrapFactoryImpl.java:53)
at org.mozilla.javascript.WrapFactory.wrap(WrapFactory.java:105)
at org.mozilla.javascript.ScriptRuntime.toObject(ScriptRuntime.java:962)
at org.mozilla.javascript.ScriptRuntime.toObjectOrNull(ScriptRuntime.java:918)
at org.mozilla.javascript.ScriptRuntime.getPropFunctionAndThis(ScriptRuntime.java:2213)
at org.mozilla.javascript.optimizer.OptRuntime.callProp0(OptRuntime.java:117)
at org.mozilla.javascript.gen.c1._c0(/view.mayaa#write:3)
at org.mozilla.javascript.gen.c1.call(/view.mayaa#write)
at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:398)
at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3065)
at org.mozilla.javascript.gen.c1.call(/view.mayaa#write)
at org.mozilla.javascript.gen.c1.exec(/view.mayaa#write)
at org.seasar.mayaa.impl.cycle.script.rhino.TextCompiledScriptImpl.normalExecute(TextCompiledScriptImpl.java:126)
at org.seasar.mayaa.impl.cycle.script.rhino.TextCompiledScriptImpl.execute(TextCompiledScriptImpl.java:166)
at org.seasar.mayaa.impl.engine.processor.WriteProcessor.writeValue(WriteProcessor.java:109)
at org.seasar.mayaa.impl.engine.processor.WriteProcessor.writeStartElement(WriteProcessor.java:161)
at org.seasar.mayaa.impl.engine.processor.AbstractAttributableProcessor.processStart(AbstractAttributableProcessor.java:185)
at org.seasar.mayaa.impl.engine.processor.AbstractAttributableProcessor.doStartProcess(AbstractAttributableProcessor.java:169)
at org.seasar.mayaa.impl.engine.RenderUtil.renderTemplateProcessor(RenderUtil.java:134)
at org.seasar.mayaa.impl.engine.RenderUtil.renderProcessorTree(RenderUtil.java:264)
at org.seasar.mayaa.impl.engine.TemplateImpl.doTemplateRender(TemplateImpl.java:200)
at org.seasar.mayaa.impl.engine.PageImpl.renderTemplate(PageImpl.java:241)
at org.seasar.mayaa.impl.engine.RenderUtil.renderPage(RenderUtil.java:349)
at org.seasar.mayaa.impl.engine.PageImpl.doPageRender(PageImpl.java:189)
at org.seasar.mayaa.impl.engine.EngineImpl.doPageService(EngineImpl.java:371)
at org.seasar.mayaa.impl.engine.EngineImpl.doService(EngineImpl.java:493)
at org.seasar.mayaa.impl.MayaaServlet.doService(MayaaServlet.java:97)
at org.seasar.mayaa.impl.MayaaServlet.doGet(MayaaServlet.java:80)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:627)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:679)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:461)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:399)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:301)
at example.MyServlet.doGet(MyServlet.java:31)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:627)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:172)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:174)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:875)
at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
at java.lang.Thread.run(Thread.java:595)

注目点

ログの

example.MyBean$$EnhancerByGuice$$c8e25a70#setNamestart
example.MyBean$$EnhancerByGuice$$c8e25a70#setNameend

という箇所に注目していただくと、Guiceによる、AOPの挿入は成功していることがわかります。

また、

2009/09/06 12:34:25 org.seasar.mayaa.impl.engine.error.TemplateErrorHandler doErrorHandle

の箇所から、MayaaServletにフォワードされていることもわかります。

スタックトレースを読むと、

java.lang.NullPointerException
at org.mozilla.javascript.net.sf.retrotranslator.runtime.impl.MethodDescriptor.getInstance(MethodDescriptor.java:137)
at org.mozilla.javascript.net.sf.retrotranslator.runtime.java.lang.reflect._Constructor.isVarArgs(_Constructor.java:83)
at org.mozilla.javascript.jdk15.VMBridge_jdk15.isVarArgs(VMBridge_jdk15.java:66)
at org.mozilla.javascript.MemberBox.init(MemberBox.java:86)

JavaScriptエンジン内で問題が発生しています。

また、MyModuleのconfigure()を

@Override
	protected void configure() {
//		bindInterceptor(
//				Matchers.subclassesOf(MyBean.class),
//				Matchers.any(),
//				new MyIntercepter());
	}

のようにコメントアウトすると、エラーが出なくなりました。

私の予想では、JavaオブジェクトをJavaScriptオブジェクトに変換しようとしたタイミングで、Guiceが作成したオブジェクトのクラス情報が、クラスローダ内に何らかの理由で残っておらず(リクエストの中のMyBeanはもはやexample.MyBeanではない)、取得できず、ヌルポンで落ちていると思います。

Mayaaの問題というより、JavaScriptエンジンの問題なのですが、MayaaにバンドルされているJavaScriptエンジンは最新(rhino-1.7r2)ですし、今後GuiceMayaaを組み合わせて使いたい人が現れるかもしれないため、報告したいと思った次第です。

ちなみに、MayaaSeasarプロジェクトなので、AOPはS2Containerを使った方が無難だったかもしれませんね:-)