View層にMayaaを使っているアプリケーションで、AOP的な機能が必要になり、Guice 2.0 を試したところ、はまってしまいました。
MLに質問したところ、簡単に試せるサンプルが欲しいとのことなので、作りましたが、公開する場所がないので、とりあえずここに書きます。
環境
以下の環境でテストしました。
- Mayaa 1.1.26
- Guice 2.0
- Tomcat 5.5.27 (JREはJava 1.5.0_15)
プロジェクト作成
プロジェクトのベースとして、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>
実行結果:
- ttp://localhost:8080/mayaa-guice/myservlet?name=hoge&aop=false
message:hello,hoge. it'sSun Sep 06 12:33:40 JST 2009
- ttp://localhost:8080/mayaa-guice/myservlet?name=hoge&aop=true
こちらは画面は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)ですし、今後GuiceとMayaaを組み合わせて使いたい人が現れるかもしれないため、報告したいと思った次第です。
ちなみに、MayaaはSeasarプロジェクトなので、AOPはS2Containerを使った方が無難だったかもしれませんね:-)