[Seasar-user:21033] Re: [S2DBCP]minPoolSizeを設定するとNullPointerExceptionが発生する

koba168 [E-MAIL ADDRESS DELETED]
2011年 9月 27日 (火) 15:46:58 JST


小林 (koichik) 様

お世話になります。小林 (koba168) です。

修正していただいた部分を確認しましたが、まだ問題があるように思います。

org.seasar.extension.dbcp.impl.ConnectionPoolImpl$FreeItem#expired()

        public void expired() {
            synchronized (ConnectionPoolImpl.this) {
                if (freePool.size() > minPoolSize) {
                    freePool.remove(this);
                }
            }
            synchronized (this) {
                if (timeoutTask_ != null) {
                    timeoutTask_.cancel();
                    timeoutTask_ = null;
                }
                if (freePool.size() > minPoolSize &&
connectionWrapper_ != null) {
                    connectionWrapper_.closeReally();
                    connectionWrapper_ = null;
                }
            }
        }

例えば freePool=1, minPoolSize=0 の状態で expired() が呼ばれた場合、
最初の freePool.size() > minPoolSize は true なので freePool.remove(this)
が呼ばれますが、ここで freePool=0, minPoolSize=0 となるため
次の freePool.size() > minPoolSize は false となり
connectionWrapper_.closeReally() が呼ばれません。
「物理的なコネクションを閉じました」のログが出ないまま、プールからは削除されます。


以下のような感じで修正すれば問題なさそうですがいかがでしょうか。

        public void expired() {
            boolean removed = false;
            synchronized (ConnectionPoolImpl.this) {
                if (freePool.size() > minPoolSize) {
                    freePool.remove(this);
                    removed = true;
                }
            }
            synchronized (this) {
                if (timeoutTask_ != null) {
                    timeoutTask_.cancel();
                    timeoutTask_ = null;
                }
                if (removed && connectionWrapper_ != null) {
                    connectionWrapper_.closeReally();
                    connectionWrapper_ = null;
                }
            }
        }


よろしくお願いいたします。



2011年9月26日17:46 Koichi Kobayashi <[E-MAIL ADDRESS DELETED]>:
> 小林 (koichik) です.
>
> 報告ありがとうございます。
> 修正して SNAPSHOT をデプロイしたのでご確認ください。
> # 修正は s2-extension のみ。
>
> http://maven.seasar.org/maven2-snapshot/org/seasar/container/s2-framework/2.4.45-SNAPSHOT/s2-framework-2.4.45-20110926.084256-1.jar
> http://maven.seasar.org/maven2-snapshot/org/seasar/container/s2-extension/2.4.45-SNAPSHOT/s2-extension-2.4.45-20110926.084256-1.jar
> http://maven.seasar.org/maven2-snapshot/org/seasar/container/s2-tiger/2.4.45-SNAPSHOT/s2-tiger-2.4.45-20110926.084504-1.jar
> http://maven.seasar.org/maven2-snapshot/org/seasar/container/s2jdbc-gen/2.4.45-SNAPSHOT/s2jdbc-gen-2.4.45-20110926.084536-1.jar
>
>
> Date: Mon, 26 Sep 2011 15:57:34 +0900
> From: koba168 <[E-MAIL ADDRESS DELETED]>
> Subject: [Seasar-user:21031] [S2DBCP]minPoolSizeを設定するとNullPointerExceptionが発生する
>
>> お世話になります。小林と申します。
>>
>> S2DBCPのコネクションプーリング機能でNullPointerExceptionが発生するケース
>> が見つかりましたので報告いたします。
>>
>>
>> 環境:
>> Seasar2 2.4.44
>>
>> jdbc.dicon:
>>
>> <component name="connectionPool"
>>       class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl">
>>       <property name="timeout">60</property>
>>       <property name="maxPoolSize">10</property>
>>       <property name="minPoolSize">1</property>
>>       <property name="allowLocalTx">true</property>
>>       <destroyMethod name="close"/>
>> </component>
>>
>>
>> スタックトレース:
>>
>> java.lang.NullPointerException
>>       at org.seasar.extension.dbcp.impl.ConnectionPoolImpl.checkOutFreePool(ConnectionPoolImpl.java:400)
>>       at org.seasar.extension.dbcp.impl.ConnectionPoolImpl.checkOut(ConnectionPoolImpl.java:364)
>>       at org.seasar.extension.dbcp.impl.DataSourceImpl.getConnection(DataSourceImpl.java:59)
>>       at org.seasar.doma.internal.jdbc.util.JdbcUtil.getConnection(JdbcUtil.java:40)
>>       at org.seasar.doma.internal.jdbc.command.SelectCommand.execute(SelectCommand.java:53)
>>       ... 以下省略 ...
>>
>>
>>
>> 再現させる手順ですがminPoolSizeに1以上の値を設定した状態で
>>
>> 1. プールされたコネクションがtimeoutで指定された時間を経過
>> 2. expired処理実行。プールされたコネクション数がminPoolSizeを下回りプールから削除しない
>> 3. そのコネクションをプールから取得して使用する
>>
>> の流れでNullPointerExceptionが発生します。
>>
>>
>> ソースを確認したところ以下のexpiredの処理が問題だと思われます。
>>
>> org.seasar.extension.dbcp.impl.ConnectionPoolImpl$FreeItem#expired()
>>
>>         public void expired() {
>>             synchronized (ConnectionPoolImpl.this) {
>>                 if (freePool.size() > minPoolSize) {
>>                     freePool.remove(this);
>>                 }
>>             }
>>             synchronized (this) {
>>                 if (timeoutTask_ != null) {
>>                     timeoutTask_.cancel();
>>                     timeoutTask_ = null;
>>                 }
>>                 if (connectionWrapper_ != null) {
>>                     connectionWrapper_.closeReally();
>>                     connectionWrapper_ = null;
>>                 }
>>             }
>>         }
>>
>>
>> freePoolのサイズがminPoolSizeを下回る場合に、freePool.remove(FreeItem)は呼ばれないが
>> FreeItemの内部変数として保持しているconnectionWrapper_については必ずクローズしてnullを設定するので
>> 次回、このFreeItemがfreePoolリストから取り出されてconnectionWrapper_が使用されるときに
>> NullPointerExceptionが発生する。
>>
>>
>> とりあえずこの部分を修正してこの問題は回避しています。
>> よろしくお願いいたします。
>> _______________________________________________
>> Seasar-user mailing list
>> [E-MAIL ADDRESS DELETED]
>> https://ml.seasar.org/mailman/listinfo/seasar-user
>
> --
> {
>  name: "Koichi Kobayashi",
>  mail: "[E-MAIL ADDRESS DELETED]",
>  blog: "http://d.hatena.ne.jp/koichik/",
>  twitter: "@koichik"
>  }
>
> _______________________________________________
> Seasar-user mailing list
> [E-MAIL ADDRESS DELETED]
> https://ml.seasar.org/mailman/listinfo/seasar-user
>


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