hakobera's blog

技術メモ。たまに雑談

WicketでS2JDBCのAbstractServiceが動かないので回避方法を考えてみた

最近、仕事で Wicket を使っているのだが、S2JDBCと組み合わせたときに AbstractService がうまくインジェクションできない、という問題が発生したので、その現象と回避方法を書いておく。
ちなみに、Wicket 1.4RC-2 + S2JDBC 2.4.34 + wicket-seasar2 1.0 beta1の組み合わせ。

問題としては、AbstractService 継承クラスをインスタンス化して、Pageクラスのフィールドにインジェクションする時に IllegalArgumentException が発生するという現象で、具体的には以下のような感じ。

WicketMessage: Can't instantiate page using constructor public sample.web.page.EmployeeListPage()

Root cause:

java.lang.IllegalArgumentException: Protected method: setEntityClass(Ljava/lang/Class;)V
at net.sf.cglib.proxy.MethodProxy$1.invoke(MethodProxy.java:55)
at org.apache.wicket.proxy.LazyInitProxyFactory$CGLibInterceptor.intercept(LazyInitProxyFactory.java:319)
at WICKET_sample.service.EmployeeService$$EnhancerByCGLIB$$31ec3fd6.setEntityClass(<generated>)
at org.seasar.extension.jdbc.service.S2AbstractService.<init>(S2AbstractService.java:73)
at sample.service.AbstractService.<init>(AbstractService.java:19)
at sample.service.EmployeeService.<init>(EmployeeService.java:14)
at WICKET_sample.service.EmployeeService$$EnhancerByCGLIB$$31ec3fd6.<init>(<generated>)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at net.sf.cglib.core.ReflectUtils.newInstance(ReflectUtils.java:228)
at net.sf.cglib.core.ReflectUtils.newInstance(ReflectUtils.java:220)
at net.sf.cglib.core.ReflectUtils.newInstance(ReflectUtils.java:216)
at net.sf.cglib.proxy.Enhancer.createUsingReflection(Enhancer.java:640)
at net.sf.cglib.proxy.Enhancer.firstInstance(Enhancer.java:538)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:225)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
at org.apache.wicket.proxy.LazyInitProxyFactory.createProxy(LazyInitProxyFactory.java:174)
at jp.javelindev.wicket.seasar2.Seasar2FieldValueFactory.getFieldValue(Seasar2FieldValueFactory.java:37)
at org.apache.wicket.injection.Injector.inject(Injector.java:108)
at org.apache.wicket.injection.ConfigurableInjector.inject(ConfigurableInjector.java:39)
at org.apache.wicket.injection.ComponentInjector.onInstantiation(ComponentInjector.java:52)
at jp.javelindev.wicket.seasar2.Seasar2ComponentInjector.onInstantiation(Seasar2ComponentInjector.java:50)
at org.apache.wicket.Application.notifyComponentInstantiationListeners(Application.java:1027)
at org.apache.wicket.Component.<init>(Component.java:911)

で、回避方法は、AbstractService の setEntityClass(Class) メソッドをオーバーライドして、アクセス修飾子を
publicに変更するだけ。

public abstract class AbstractService<ENTITY extends EntityBase> extends S2AbstractService<ENTITY> {

    /**
     * wicket-ioc で利用している cglib で protected なメソッドが呼び出せないので、
     * オーバーライドして、public にしておくが、アプリケーションからは呼び出していはいけない。
     * 
     * @deprecated このメソッドを呼び出しは禁止
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.NEVER)
    public void setEntityClass(Class<ENTITY> entityClass) {
        super.setEntityClass(entityClass);
    }
    
}

ちなみに、Javadocの deprecated は万が一呼び出されても検出できるように、TransactionAttributeアノテーショントランザクション管理から外すため(デフォルトで、publicなメソッドは全てトランザクション管理される設定になっているため)に指定している。

原因は深くは追求していないのだが、wicket-ioc が利用している cglib の仕様で、protectedメソッドが呼び出せないためだと思われる。
例外から protected なメソッドは呼び出せないよ、というニュアンスを感じたのでやってみたらできたので、本当かどうかは不明。今のところ問題なく動いているので、時間があるときにもう少し追求することにする。

この組み合わせって結構需要がありそうなのだが、なぜか情報がない。
たぶん、Wicket好きの人はGuice好きだからで、Seasar2好きな人はSAStruts好きってことなんだろうと、個人的には納得しているのだが、実際のところはどうなんだろう。