Monthly Archives: 9月 2009

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

http://d.hatena.ne.jp/s-ishigami/20090906/1252210901 の続きです。
MLにてsugaさんに回答いただき、無事解決することができました。感謝です。

解決方法

Mayaa Blank Warにバンドルされているrhino-1.7r.jarを使用せず、下記URLよりRhino最新版を入手して、その中のjs.jarを使用する。
http://www.mozilla-japan.org/rhino/download.html

解説

Rhinoには、js.jarとjs-14.jarの2種類のjarファイルが存在し、前者はJava5以上、後者はJava1.4でも動くとのことです。

Guiceを使っているならJava5なので、Guice+Mayaaの組み合わせはこれで問題なさそうです。

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

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を使った方が無難だったかもしれませんね:-)