[Seasar-user:21058] Re: 【Doma】Seasar連携時の複数データソース動的切替方法について

Toshihiro Nakamura [E-MAIL ADDRESS DELETED]
2011年 10月 10日 (月) 22:39:34 JST


中村(taedium)です。

> また、デバッグ実行で追ってみた限りでは、AnyTableDaoImpl(doma...AbstractDao)のコンストラクタから
> RuntimeConfigをインスタンス化する際の
> AssertionUtil.assertNotNull(config.getDataSourceName()); にて
> 前述のようにnullで取得されるため、エラーが発生しているようでした。

この挙動は、Domaのバグですね。。。
修正してSNAPSHOTを作成したのでdoma-1.18.0.jarの代わりに
次のjarで試していただけないでしょうか。
http://maven.seasar.org/maven2-snapshot/org/seasar/doma/doma/1.19.0-SNAPSHOT/doma-1.19.0-20111010.130704-1.jar

それと、今回の現象と直接は関係ありませんが、
インターセプタをカスタマイザで設定する際useLookupAdapterをtrueにする必要があります。
(そうしないと、COOL deploy時に、インターセプタがprototypeとして動作しません)
設定方法は次のリンク先が参考になります。
http://www.seasar.org/wiki/index.php?FAQ%2FS2AOP#q69751d4

2011年10月10日12:29 . <[E-MAIL ADDRESS DELETED]>:
> 返信ありがとうございます。
> okduaです。
>
>> 「getComponentによって取得する」とありますが、これは、
>> net.arnx.jsonic.web.extension.S2ContainerクラスのgetComponentメソッド
>> の中で行われる処理を指しているのでしょうか?
> 申し訳ありません。
> net.arnx.jsonic.web.extension.S2ContainerクラスのgetComponentを指していました。
> (補足ですが、net.arnx.jsonic.web.RESTServletの中では、S2ContainerからSingletonS2ContainerFactoryへ
> 委譲してgetComponentからcomponentを取得し、別途呼び出すメソッド名を決定してから、
> componentにexecuteをかけているようです。)
>
>> SelectDataSourceInterceptorはS2コンテナに登録できていますか?
> はい、パッケージ名.interceptor.SelectDataSourceInterceptorとして作成しております。
> また、creator.dicon には、InterceptorCreator および ActionCreatorを
> 定義しております。
> (Service/DAOについても、該当のCreatorを定義し、自動登録を利用しています。)
>
>> コンストラクタとgetComponentは別物ですし、それらの前に実行させるというの
>> とはちがうんじゃないでしょうか。
> ActionCreatorを利用した場合、Actionは request スコープで作成されると
> リファレンスにありましたので、てっきりgetComponentがコールされた時に
> インスタンス化を行っているものと思っていました。
> また、Actionインスタンス化時に、Serviceのインスタンス化も行われ、
> 更にその中でDAOのインスタンス化が行われ、、と伝播するものと解釈していました。
> (スコープによってsingletonで準備するものは、インスタンス化されないという意味
>  で、厳格には別物と表現されているのでしたら、すみません。)
>
> 何故、コンストラクタと私が思ったのかというと、
> ・DAOを利用するAction (厳密には「DAOを利用するService」を利用するAction)
> ・DAOを利用しないAction
> の二つで試してみたところ、後者のDAOを利用しないActionでは、
> SelectDataSourceInterceptor.invoke()が実行されたためです。
> (実行はブレークポイント&ログ出力で確認しました。)
> また、前者のDAOを利用するActionの場合は、
> 下記のようなログが出力されていました。
> ですので、実際のリクエストに対するメソッドの実行の前にDAOのインスタンス化が行われ、
>     Dao.constructor() → SelectDataSourceInterceptor.invoke()
> のようになってしまい、SelectDataSourceInterceptor.invoke()の前にAppConfigからDataSourceNameを
> 取得しようとしているのではないか?と解釈したためです。
> また、デバッグ実行で追ってみた限りでは、AnyTableDaoImpl(doma...AbstractDao)のコンストラクタから
> RuntimeConfigをインスタンス化する際の
> AssertionUtil.assertNotNull(config.getDataSourceName()); にて
> 前述のようにnullで取得されるため、エラーが発生しているようでした。
>
> あと一歩のような気がするのですが、どう設定すればよいのか分からない次第です。。
> 言葉足らずな部分もあるかと思いますが、ご教示いただけると幸いです。
>
>
> 【ログ】
> ...
> クラス(foo.bar.package.action.LoginAction[login_loginAction])のコンポーネント定義を登録します
> ...
> クラス(foo.bar.package.interceptor.SelectDataSourceInterceptor[selectDataSourceInterceptor])のコンポーネント定義を登録します
> ...
> クラス(foo.bar.package.service.LoginService[login_loginService])のコンポーネント定義を登録します
> ...
> クラス(foo.bar.package.dao.base.impl.AnyTableDaoImpl[base_anyTableDao])のコンポーネント定義を登録します
>
> 以上、よろしくお願いいたします。
>
> 2011年10月10日4:29 Toshihiro Nakamura <[E-MAIL ADDRESS DELETED]>:
>>
>> 中村(taedium)です。
>>
>> > クライアントからリクエストが来た際、ActionをgetComponentによって取得する
>> > のですが、同じタイミングでService/DAOもインスタンス化されることを
>> > 確認しました。
>>
>> 「getComponentによって取得する」とありますが、これは、
>> net.arnx.jsonic.web.extension.S2ContainerクラスのgetComponentメソッド
>> の中で行われる処理を指しているのでしょうか?
>> アプリケーションで行っている処理を指しているのかJSONICによる処理を
>> 指しているのかわかりませんでした。
>>
>> SelectDataSourceInterceptorはS2コンテナに登録できていますか?
>> SMART deployを使って登録するのであれば、
>> SelectDataSourceInterceptorが「ルートパッケージ名.interceptor」という
>> 名前のパッケージに配置されていることを確認してください。
>> さらに、creator.diconに次の記述があることを確認してください。
>> <component
>> class="org.seasar.framework.container.creator.InterceptorCreator"/>
>>
>> creator.diconで以下の記述はされていますか?
>> <component class="org.seasar.framework.container.creator.ActionCreator"/>
>> この記述がないと、customizer.diconのactionCustomizerの設定が反映されません。
>> (ActionをSMART deployで登録しているのであれば記述されているはずですが)
>>
>> > しかし、Interceptorが実行されず、DAOにてConfigからDatasourceNameを取得
>> > できないというエラーが発生してしまいました。
>>
>> 念のために、Interceptorは実行されているけれども
>> DatasourceNameにnullが設定されることでエラーが発生している、
>> という可能性はないか確認してみるとよいかもしれません。
>>
>> > Interceptorは、コンストラクタ(getComponent)の前に実行させることが
>> > できないのでしょうか?
>>
>> Interceptorを使うと特定のメソッド呼び出しの前後で任意の処理を実行できます。
>> 今回やりたいことはまさにそれだと思います(Actionのメソッドの前後で処理をする)。
>> コンストラクタとgetComponentは別物ですし、それらの前に実行させるというの
>> とはちがうんじゃないでしょうか。
>>
>> 要件に対して、現在やろうとしていることの方向性はまちがっていないと思います。
>> 何らかの理由で、InterceptorがActionに適用されていないようですが
>> 適切に設定すれば解決できると思います。
>>
>> 2011年10月9日22:00  <[E-MAIL ADDRESS DELETED]>:
>> > はじめまして。
>> > okudaと申します。
>> >
>> > 現在、Seasar2+Doma(+JSONIC REST Servlet)にて、開発を行なっております。
>> > データ構造が同じでスキーマが異なるDBへ動的に切り替えてアクセスしたいので
>> > すが、上手く動作しなく躓いております。
>> > 大変お手数ですが、ご教示いただけると幸いです。
>> >
>> > 【環境】
>> > ・JDK 1.6u27
>> > ・Tomacat 6.0.29
>> > ・Seasar2 2.4.44
>> > ・Doma 1.18.0
>> > ・JSONIC 1.2.7
>> > ・JDBC4 Postgresql Driver, Version 9.1-901
>> >
>> > 【要件】
>> > ・リクエストURIによって、接続するスキーマを動的に変更する
>> > ・DIを行う際はEJB3準拠で行う
>> >
>> > 【試した方法】
>> > ・Doma JPetStoreを参考に、demo.configパッケージのものをそのまま流用
>> >
>> > (AppConfig,CommonsJdbcLogger,S2RequiresNewController,SqlFileRepositoryProxy)
>> > DAOには、@Dao(config = AppConfig.class)アノテーションを付与しています。
>> > ・「同じ種類のデータソースを動的に切り替える」
>> > http://s2container.seasar.org/2.4/ja/jdbc.html#DataSourceDynamicSwiching
>> > を参考に、下記【作成ソース】のようにdiconおよびInterceptorを作成
>> >
>> > 【質問】
>> > 1.
>> > Interceptor→Action→Service→DAOとDIを用いてアクセスしていくことを
>> > 想定しています。
>> > クライアントからリクエストが来た際、ActionをgetComponentによって取得する
>> > のですが、同じタイミングでService/DAOもインスタンス化されることを
>> > 確認しました。
>> > しかし、Interceptorが実行されず、DAOにてConfigからDatasourceNameを取得
>> > できないというエラーが発生してしまいました。
>> > Interceptorは、コンストラクタ(getComponent)の前に実行させることが
>> > できないのでしょうか?
>> >
>> > 2.
>> > 仮にできない場合、どのようなアプローチを取ることが好ましいのでしょうか?
>> >
>> >
>> > 【作成ソース】
>> > [user1-jdbc.dicon および user2-jdbc.dicon]
>> > (user2-jdbc.diconはuser2DataSourceとしてDataSourceImplを定義し、
>> >  後は、下記user1-jdbc.diconと同じ)
>> > <components namespace="jdbc">
>> >  <include path="jta.dicon"/>
>> >  <include path="jdbc-extension.dicon"/>
>> >
>> >  <component name="user1DataSource"
>> >     class="org.seasar.extension.dbcp.impl.DataSourceImpl"/>
>> >  <component
>> > class="org.seasar.extension.jdbc.impl.BasicResultSetFactory"/>
>> >  <component
>> > class="org.seasar.extension.jdbc.impl.ConfigurableStatementFactory">
>> >    <arg>
>> >        <component
>> > class="org.seasar.extension.jdbc.impl.BasicStatementFactory"/>
>> >    </arg>
>> >    <property name="fetchSize">100</property>
>> >  </component>
>> >
>> >  <!-- for PostgreSQL -->
>> >  <component name="xaDataSource"
>> >    class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
>> >    <property name="driverClassName">"org.postgresql.Driver"</property>
>> >    <property name="URL">
>> >        "jdbc:postgresql://localhost/TEST"
>> >    </property>
>> >    <property name="user">"user1"</property>
>> >    <property name="password">"xxxx"</property>
>> >  </component>
>> >  <component name="connectionPool"
>> >    class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl">
>> >    <property name="timeout">600</property>
>> >    <property name="maxPoolSize">10</property>
>> >    <property name="allowLocalTx">true</property>
>> >    <destroyMethod name="close"/>
>> >  </component>
>> > </components>
>> >
>> > [jdbc.dicon]
>> > <components namespace="jdbc">
>> >    <include path="user1-jdbc.dicon"/>
>> >    <include path="user2-jdbc.dicon"/>
>> >
>> >    <component name="dataSource"
>> > class="org.seasar.extension.datasource.impl.SelectableDataSourceProxy"/>
>> >    <component name="dataSourceFactory"
>> > class="org.seasar.extension.datasource.impl.DataSourceFactoryImpl"/>
>> > </components>
>> >
>> > [SelectDataSourceInterceptor]
>> > public class SelectDataSourceInterceptor implements MethodInterceptor {
>> >    @Resource
>> >    public DataSourceFactory dataSourceFactory;
>> >    @Resource
>> >    private HttpServletRequest request;
>> >
>> >    @Override
>> >    public Object invoke(MethodInvocation invocation) throws Throwable {
>> >        String currentName =
>> > dataSourceFactory.getSelectableDataSourceName();
>> >        try {
>> >            String dataSourceName = getDataSourceName();
>> >
>> >  dataSourceFactory.setSelectableDataSourceName(dataSourceName);
>> >            return invocation.proceed();
>> >        } finally {
>> >            dataSourceFactory.setSelectableDataSourceName(currentName);
>> >        }
>> >    }
>> >
>> >    public String getDataSourceName() {
>> >        // request からURIを取得し、対象DataSource名("user1" or "user2")
>> >        // を決定
>> >    }
>> > }
>> >
>> > [customizer.dicon抜粋]
>> >  <component name="actionCustomizer"
>> > class="org.seasar.framework.container.customizer.CustomizerChain">
>> >    <initMethod name="addAspectCustomizer">
>> >      <arg>"selectDataSourceInterceptor"</arg>
>> >    </initMethod>
>> >    ....
>> >  </component>
>> >
>> >
>> > 以上、よろしくお願いいたします。
>> >
>> > _______________________________________________
>> > Seasar-user mailing list
>> > [E-MAIL ADDRESS DELETED]
>> > https://ml.seasar.org/mailman/listinfo/seasar-user
>> >
>>
>>
>>
>> --
>> Toshihiro Nakamura
>> _______________________________________________
>> Seasar-user mailing list
>> [E-MAIL ADDRESS DELETED]
>> https://ml.seasar.org/mailman/listinfo/seasar-user
>
>
> _______________________________________________
> Seasar-user mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-user
>
>



-- 
Toshihiro Nakamura


Seasar-user メーリングリストの案内