めざましテレビ

今日の早耳ムスメは天使の食レポーター臼田あさ美ちゃーん,お題は「ユニークものが続々! 2005 カレンダー」.
っていうか,食レポじゃありませんから!! 残念!!!!
でもでも,食べてなくても楽しそうな表情がやっぱりカワイイですねぇ♪
ローサちゃんのカレンダーの紹介も.もなじろうさん買うしか!!

コネクションプーリングと S2DBCP

without-ejb ML での話題ですが,なんとなくこっちへ.(^^;

ひとつわからないのですが、
S2SessionFactoryImpl#beforeCompletion()の呼び出しで
Connectionが閉じられるのはどうしてでしょうか。
Transactionのcommitの前にConnectionを閉じるのは問題なのではと
思ってしまいます。
たぶんTransactionの理解が足らないのですね…

トランザクションというよりコネクションプーリングかなぁ.


コネクションプーリングを理解する上で重要なのは,物理コネクションと論理コネクションの区別だと思います.物理コネクションというのは DBMS 等に対する本当のコネクションで,論理コネクションはそのラッパーのようなものと考えればよいでしょう.
JDBC においては,次の interface が定められています.

  • javax.sql.PooledConnection

これは,プーリング可能な物理コネクションを表現します.こいつのメソッド

    • Connection getConnection()

は,その物理コネクションに対するハンドル,すなわち論理コネクション (Connection) を返します.
論理コネクションの close() を呼び出しても,それは物理コネクションを閉じるとは限りません.っていうか,通常は閉じません.その代わり (?) ConnectionEvent が発生します.このイベントは,PooledConnection

    • void addConnectionEventListener(ConnectionEventListener)

でリスナーを登録することで受け取ることが出来ます.このイベントは本来,プーリング可能なコネクション (PooledConnection の実装) の提供者 (たいていは DBMS のベンダ) とコネクションプールの提供者 (アプリケーションサーバのベンダとか) が異なる場合に連携できるようにすることを意図していると思います.その単純なシナリオは次のようなものだと思われます.

  • コネクションプール (おそらく DataSource を実装している) は,物理コネクション (PooledConnection を実装している) のインスタンスをプーリングしています.このコネクションプールは ConnectionEventListener を実装しており,コネクションプール自身を物理コネクションに addConnectionEventLister() しています.
  • アプリケーションがコネクションプールから getConnection() などすると,コネクションプールは物理コネクションをプールから取り出し,その getConnection() を呼び出します.そして戻り値である論理コネクション (Connection を実装している) をアプリケーションに返します.
  • アプリケーションが論理コネクションの close() を呼び出すと,PooledConnection は登録されているリスナー (コネクションプール) の ConnectionEventLister#connectionClosed(ConnectionEvent) を呼び出します.
  • コネクションプールは物理コネクションをプールに戻します.

このようなシナリオは JDBC だけでなく,JCA でも概ね同様です.


現実には,スレッドコンテキストやトランザクションコンテキストが絡んでくるため,もっと複雑になります.つまり,あるスレッド上で使われる DAO などではどれも同じ (物理) コネクションを使いたいとか,同じトランザクションで使われる DAO などでは同じ (物理) コネクションを使いたいということになるため,論理コネクションの close() が呼ばれた際に安易にプールに戻していいとは限らないからです.
そこで,物理コネクションと論理コネクションの関係を (概念上) 1 対多と見なすことが出来ます (実際にそう実装するとは限りませんが).たぶん,ここが重要.つまり,同じトランザクションに参加しているいくつかの DAO や Hibernate のセッションなどが,それぞれコネクションプール (たいていは DataSource ですが) から論理コネクションを取得した場合,それらが異なった論理コネクションのインスタンスであったとしても,実際には同じ物理コネクションと関連づけられているということです.どの論理コネクションを close() しても,物理コネクションは閉じられませんし,プールに戻されることもありません.それはコンテキストの終了時 (例えばトランザクションがコミットされた後) に行われます.ですから,close() を呼び出さなくても平気 (結果オーライ) だったりするわけです.
とはいえ,DAO であれ Hibernate のセッションであれ,自分が取得したコネクションを使い終えたらクローズするのがマナーというものではないでしょうか? でないと,トランザクションマネージャと連携しなければコネクションプーリングも提供しないような DataSource の場合に誰もコネクションをクローズしてくれなさそう.そんなわけで (どんなわけで?),S2SessionFactoryトランザクションがコミットされる直前にセッションを flush した後,自分が持っているコネクションをクローズしているのでしょう.同じように S2Dao の S2DaoInterceptor も DB アクセスの度に DataSource から取得したコネクションをクローズしています.


というのが基本的な考え方だと思いますが,実際のコネクションプールには様々な実装があって,例えば S2DBCP では物理コネクションと論理コネクションは (おそらく) 1 対 1 で,論理コネクションをプールしているようです.基本的な構造は次のようになっています.

  • DataSourceImpl
  • ConnectionPoolImpl
  • XADataSource (Impl)
  • (DriverManager)

下から見ていくと,本物の XADataSource を使わない場合は,DriverManager が物理コネクション (Connection) を作成します.それを XADataSource のように見せかけてくれるのが XADataSourceImpl ですが,本物の XADataSource を使うこともできます.これは XAConnection を返します.XAConnectionPooledConnectionextends した interface で,本来は物理コネクションですが,DriverManager から Connection を取得した場合にはそのラッパー (XAConnectionImpl) です.でも,概念的には物理コネクション.
その上位にあるのが ConnectionPoolImpl で,物理コネクション (XAConnection) から取得した論理コネクション (Connection) のラッパー (ConnectionWrapperImpl) を作成し,プーリングします.そう,S2DBCP でプーリングされるのは物理コネクションではなくて論理コネクションです.最初見た時,ちょっと意外で驚いた部分です.
そしてさらにその上位にあるのが DataSourceImpl で,これは ConnectionPoolImpl に対する薄いラッパーで,アプリケーションが DataSource として使うものです.普通 DataSource なプロパティに DI されるのはこのインスタンスです.
この中で,ConnectionPoolImplJTASynchronizationimplements していて,TransactionManager と連携する,S2DBCP の中枢です.アプリケーションが DataSource#getConnection() を呼び出すと,DataSourceImplConnectionPoolImpl から論理コネクションを取得します.ConnectionPoolImpl は,TransactionManager から現在の Transaction を取得し,それをキーとしてアクティブなコネクション (使われているコネクション) のプールから論理コネクションを探し,もしあればそれを返します.なければフリーなコネクション (使われていないコネクション) のプールから論理コネクションを取り出し (場合によっては新しいコネクションを作成し),アクティブなコネクションのプールに入れてそれを DataSourceImpl に返します.これでアプリケーションはめでたく論理コネクションを手に入れることが出来るわけです.同じトランザクションの元で再度 DataSource#getConnection() が呼び出されると,今度はアクティブなプールから Transaction をキーとして論理コネクションを取得できます.ナイス.
そしてトランザクションが commit / rollback すると,TransactionManagerConnectionPoolImplimplements している Synchronization のメソッド afterCompletion(int) を呼び出してくれます.そこで論理コネクションはアクティブなコネクションのプールからフリーなコネクションのプールに移されます.バッチリ.
ついでに書いておくと,物理コネクション (のラッパー) である XAConnectionImplXAResourceImpl を持っていて,これが TransactionManager に enlist されます.そしてトランザクションが commit / rollback される時に TransactionManagerXAResourceImplcommit() / rollback() を呼び出し,それが物理コネクション (Connection) の commit() / rollback() を呼び出します.


ここでちょっとした疑問.
上で書いたように,S2SessionFactory が論理コネクションをクローズすることは理に適ったことであり,本来全く問題がないはずです.しかし...
実際には ConnectionPoolImpl がプーリングしているのは論理コネクション (ConnectionWrapperImpl) であり,S2SessionFactory だけでなく,同じトランザクションに参加する DAO 等でも同じ論理コネクションのインスタンスを使っています.
そして,ConnectionWrapperImpl#close() の実装は次のようになっています.

    public void close() throws SQLException {
        if (closed_) {
            return;
        }
        closed_ = true;
        connectionPool_.checkIn(this);
        logger_.log("DSSR0002", null);
    }

論理コネクションをクローズした状態にしているわけですね.
そして,例えば createStatement() など多くのメソッドは冒頭で assertOpend() というメソッドを呼び出しているのですが,その実装は次の通りです.

    private void assertOpened() throws SQLException {
        if (closed_) {
            throw new SSQLException("ESSR0062", null);
        }
    }

クローズされた状態だと例外をスローしています.
併せて考えると,S2DaoInterceptor であれ,S2SessionFactory であれ,誰かが論理コネクションをクローズすると,そのトランザクションではもはや事実上 DB アクセスが出来ないということにならないでしょうか? だって論理トランザクションはクローズされた状態になっていて,それを使うと例外が吹っ飛んでくるのですから.
しかし,実際にはそんなことはなりません.なぜでしょう?


実は,ConnectionWrapperImpl#close() が呼び出している ConnectionPoolImpl#checkIn(ConnectionWrapper) にその秘密 (おおげさ) があります.

    public synchronized void checkIn(ConnectionWrapper connection) {
        activePool_.remove(connection);
        Transaction tx = getTransaction();
        if (tx == null) {
            checkInFreePool(connection);
        } else {
            connection.init();
        }
        notify();
    }

なーんと,トランザクションコンテキストの中にいる場合は論理コネクションを再度初期化してるんですね〜.以前 S2DBCPS2Tx を眺めていた時,これに気づかなかったせいでなぜうまく動いているのか分からなかったんですよね.ひがさんのコードにしては珍しく謎めきと言ったら言い過ぎ? (^^;
いやいや,私の読解力不足です.心より恥じる.


ずいぶん長くなってしまいましたが,まとめると論理コネクションのクローズが物理コネクションのクローズを意味するわけではないということと,アプリは (フレームワークも) 自分が取得したコネクションは遠慮せずにというか忘れずにクローズしろってことです.
あ,でもそれが通用しないコネクションプールもあるって話でしたね.そこで Spring では SmartDataSource が用意されているのだとか.残念!!!!

S2Axis WSDD でもいい?

ただいま、S2Axis待ち。今のプロジェクトでバリ使うます。id:koichikさんお願いしますです。ハイ。

ま,まじっすか!?
こここ,心の準備があうあう.うーみゅ,大丈夫なのか?
っていうか,太一さんが関わったものって結構痛い目に遭う確率高いんだよね? S2Axis 選ばないでよ.(^^;
まぁ,冗談はともかくとして...

マッピングを作るの超辛いです。ハイ。早くS2Axisが出来ないと手作りしそうです、ハイ。

えと,Java 型 (FQN) から機械的XML 型 (QName) を決められる場合ならマッピング指定なしで使えると思います.とはいえ,外から WSDL を持ってきて,WSDL2Java で --NStoPkg を指定する場合にはマッピングの指定が必要.そんなわけで (どんなわけで?),昨日

どうしたものか悩んでいたら,どうやら Axis の WSDL2Java は WSDL から WSDD も生成してくれるんですね.な〜るほどぉ.そこには必要な情報が全部揃ってるもんなぁ.


そんなわけで (どんなわけで?),S2Axis では WSDL2Java を拡張した WSDL2JavaAndDicon (?) を提供することにしたいと思います.こいつは WSDL から TypeMapping を含んだ dicon ファイルを生成します.WSDL2Java は拡張できるように作られているので,WSDD を生成する JavaDeployWriter をパクって JavaDiconWriter を作って登録すればうまくいくはず.

と書いたわけですが,なんかこれ無駄な気がしてきました.
どうせなら,WSDL2Java が生成した deploy.wsdd をそのまま読んじゃだめかなぁ?
って,それだったら普通に AdminClient でデプロイしても同じか? つまり,以前提案のあったかとちんさんの案がよかったということ? やっぱり譲っておくべきだったか.(^^;
とりあえず,試行錯誤ということで実装してみました.
まず,dicon を次のように記述します.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN"
  "http://www.seasar.org/dtd/components21.dtd">
<components>
  <include path="s2axis.dicon"/>

  <component name="Foo" class="org.seasar.axis.examples.foo.FooSoapBindingImpl">
    <meta name="s2axis:rpc-service">"org/seasar/axis/examples/foo/deploy.wsdd"</meta>
  </component>
</components>

という具合に,<meta> 要素内に文字列で WSDD へのパスを記述すると,それを読み込んで Axis にデプロイします.これは AdminClient を使うのではなく,直接 Axis のエンジンに登録してます.結構簡単にできました♪ サービスだけじゃなくグローバルハンドラもこれで登録できるんじゃないかなぁ.でも,今のところ deploy.wsdd 中に <service> 要素は一つしか許されません.dicon の <meta> が付けられている ComponentDef が一つしかないので.
ちなみに <meta> の内容が省略されたり, ServiceDef だった場合には昨日書いたように動きます.お手軽にも使えて,WSDD のフル機能も使えちゃいます.
普通に Axis を使う場合,WSDL2Java で生成した deploy.wsdd

  • AdminClient でデプロイする.
  • server-config.wsdd にコピペする.

のいずれかだったと思うのですが,前者はサーバーが複数ある場合などに面倒そうだし,後者は人手ではやりたくない感じ.
それに比べると deploy.wsdd を置いておくだけで (その場所を dicon に書かないといけませんが) サーバの起動時に読み込んでくれるのでラクチンじゃないかなぁ.
どうでしょう? ご意見募集中〜.

レガシーなオブジェクト指向

レガシーっていうより

ナイーブなオブジェクト指向

かなぁ.
この「ナイーブ」は当然 (?) without EJB のアレ (笑) です.幼稚・稚拙というニュアンス.
「役割ごとにクラスを分割する」というと Responsibility-Driven かなぁって感じですが,それなら

Designing Object-Oriented Software

Designing Object-Oriented Software

という 1990 年の書籍で既に紹介されていたりします.ちなみに同じく Rebecca さんの近著はこちら.
Object Design: Roles, Responsibilities, and Collaborations (Addison-Wesley Object Technologiey Series)

Object Design: Roles, Responsibilities, and Collaborations (Addison-Wesley Object Technologiey Series)

内容? 当然積ん読です.(;_;) 心より恥じる.
ともあれ (JW),

Fatなクラスよ、さようなら

は御意.
ついつい考えるのが面倒になって既存の適当なところに機能増やしちゃったりしがちなんですけどね.心より恥じる.

恋するハニカミ!

金曜日だというのに見るのを忘れてましたよ.心より恥じる.
ともあれ (JW),今日の友里ちゃんは.. 色の名前って苦手なんだよな... このカーデは何色? 緑と水色を混ぜたような感じ? えーい,つまりこんな色だ! なんていえばいいの? 教えてエロイ人!!
とにかくそんな色のカーデガンが爽やかな感じの友里ちゃんです.中にはこれまたなんていえば分からない色だけど濃いめのベージュというか,つまりこんな色! のキャミソールらしきもの着てます.レース付きでこれもカワイイっぽい.でもってデニムスカートに濃いめ茶色のブーツ.
今日もお美しいことで... トークがなくてもいいのだ.きれいで可愛い友里ちゃんを見ることが出来たので,十二分に満足です!!
でも拙者,友里ちゃんの声を聞くために興味のないデートを 120 倍速とかにしないでちゃんと再生してますから!! 切腹!!!!
っていうか来週はスペシャル? それじゃあ友里ちゃんでないのかなぁ? しくしくしく.

出演予定 TV 番組

この近辺 (どこ?) で話題のモデルが出演するテレビ番組を分かるだけ掲載します.
新規分は赤字で (レギュラー除く).直近分は太字で.

蛯原友里
12/19 (日) 02:40〜04:30 ANB 「特命係長・只野仁 (再)」
12/22 (水) 21:00〜23:09 ANB 「特命係長・只野仁リターンズ」
山田優
12/19 (日) 15:30〜17:00 TBS 「オレンジデイズ (再)」
12/30 (木) 11:30〜12:25 TX 「感動!山田優ハワイ極上体験ツアー〜母娘の休日〜」

「特命係長」の再放送は今夜です.おいら的には DVD-BOX 持ってるのでどうという事はない気もするのですが,もしかしたらスペシャルや年明けの放送に向けて何かあるかもしれないので一応録画します♪
優ちゃんのはすごいっすね... 単独で一時間番組っすか... うらやましい.
そういえば「FRIDAY」の表紙が優ちゃんでした.「SPA!」も.カネボウの新しい CM に合わせたのか,露出が増えてる感じですね.

CanCam 01 月号 エビちゃんベストセレクション 23

CanCam2005年01月号の蛯原友里ちゃん

CanCam から,お気に入りの蛯原友里ちゃんを紹介しようというこのコーナー.
今日もゼイヴェルという通販会社 (?) とのタイアップで「恋を呼び込む激モテ BOOK」という綴じ込み付録の友里ちゃん.
珍しい角度ですね.でも,どこから見てもキレイなのです♪
そんなわけで (どんなわけで?),やっぱり CanCam 買うしか!!


新しいパソコンを購入したという友里ちゃんですが,そのせいか日記の更新が多くなってきました.ありがたいことです.
なんと,早くも春夏物の撮影が始まっているということでびっくり! これからますます寒くなるはずなのに,どんどん薄着での撮影になっていくって事ですか.大変な職業ですね...
頑張って!!