Hatena::ブログ(Diary)

日記

2004-04-15 (木)

[][] Lakers 105 - 104 TrailBlazers(OT) 03:38

\(^o^)/ \(^o^)/ \(^o^)/ \(^o^)/

シーズン最終戦,2度目の延長,シャックがファウルアウトになって残り2.2秒で2点のビハインド...

そこから残り1.1秒,コービーが逆転3ポイント!! すっげー!!!!*1

そして,一足先に負けていたキングスを逆転して地区優勝!!!!!!

もう'!'がいくつあっても足りません! ミラクルですっ!!

さぁ,いよいよプレーオフ.相手はロケッツです.楽な相手ではない(今季は2勝2敗)と思いますが,ぜひともスイープしていただきたい.頑張れー!

[] 4連休 02:20

明日から4連休です.(^^;

16日(金)と19日(月),いずれも19時から予定があるからなのですが,そのために仕事を休むとはやる気満々というのかやる気nullというのか(苦笑).

  • 16日(金) OO Enkai

明日の夜は,かつてニフティサーブのプログラマーズフォーラム(プロ)「OOについて議論する」会議室でいろいろと勉強させていただいた常連の方々と宴会です.それがOO Enkai(笑).当時はOO OFFと呼ばれていたものが,パワーアップして復活しました.どうパワーアップしたかは謎です.

ニフティのフォーラムや会議室が寂れてしまい,自分も休会して久しいこともあって,数年ぶりにお会いする人が何人も.とても楽しみです.

ちなみに,私がはてなをはじめたのは,その常連だったはぶさんまこたんを見かけた影響です.いろいろ刺激をもらって感謝してます.

  • 19日(月) 第11回日本XPユーザー会 Martin Fowler夫妻とGregor Hohpe氏を囲む会

なんでも,一緒に食事をしながらお話をするとか.しかし英語が... 無念だ.

ということで,会場で話し相手になってくれる人募集します.(^^;

募集というか,こちらが応募させていただきたいです.よろしくお願いします.>参加される方

[][][] Spring Framework 入門記 AOPその15 TargetSource 21:57

今回は「5.10. Using TargetSources」に進みます.なんか,いつのまにか5章もあと少しみたい? 確かに,Spring AOPの基本的なことは大体分かってきた感じがします.便利なautoproxyも使えるようになってきたし.よし,この調子でがんばるべし!

で,今回のTargetSourceですが,なんかちょっと分かりにくい存在です.これは,Aspectを適用するターゲットオブジェクトAOP Proxyに提供することを役割としています.DataSourceSourceと同じ意味なんでしょうね.

AOP Proxyは,メソッドが呼び出されるたびに,Adviceをかましながら最後はターゲットのメソッドを呼び出すわけですが,その都度ターゲットをTargetSourceから取得するということを,実はこれまでも水面下で行っていたようです.ふー,Springって本当に複雑だなぁ.

TargetSourceが存在することにより,例えばJDBCのConnectionにAspectをWeavingしたい,なんて場合に重宝するのかもしれません.コネクションプールを使う場合,JDBCのConnectionにAspectを適用するのはなかなか難しいように感じます.普通に考えると,コンテナが扱うのはDataSourceであってConnectionではありません.やるとしたら,Connectionのラッパー(Proxy)を作って,それにAspectをWeavingするとか.ラッパーはプロパティとしてDataSourceを持って,必要に応じて本物のConnectionを取得する,みたいな感じでしょうか.

それを一般化したものが,今回のTargetSourceなのかな? と解釈してみました.つまり,TargetSourceのメリットを平たく言うと,AspectのWeavingを本物のターゲットの取得前に行える! ということではないかと.たぶん.

さて,そのTargetSourceですが,次のメソッドを持っています.

  • Object getTarget()
  • void releaseTarget(Object target)
  • Class getTargetClass()
  • boolean isStatic()

AOP Proxyは,getTarget()でターゲットオブジェクトを取得してそのメソッドを呼び出し,その後releaseTarget(Object)でターゲットオブジェクトTargetSourceに戻します.

もし,TargetSourceが常に同じターゲットオブジェクトを返す場合は,isStatic()trueを返します.その場合,AOP Proxyは毎回getTarget()を呼び出さないように最適化するかもしれません(Springが現時点で提供するAOP Proxyはやっていないようです).

そして,Springが標準で提供するTargetSourceの実装としては,次のものがあります.

SingletonTargetSource
デフォルトで使用されるTargetSourceで,常に同じインスタンスを返します.
HotSwappableTargetSource
ターゲットを取り替えられるTargetSourceです.ターゲットを取り替えるには,swap(Object)メソッドを使用します.
CommonsPoolTargetSource
Jakarta CommonsのPoolを使用するTargetSourceです.
PrototypeTargetSource
毎回コンテナからBeanを取得して返すTargetSourceです.Beanが(singletonではなく)prototypeである場合に使う価値がありますが,AOP Proxyのメソッド呼び出しのたびにターゲットのインスタンス生成 & Dependency Injection が行われるため,良い子は使わないほうがよさげです.

これまで使ってきたProxyFactoryBeanAutoProxyCreatorを使った場合,デフォルトではSingletonTargetSourceが使われます.これを変更するには,ProxyFactoryBean#setTargetSource(TargetSource)XxxAutoProxyCreator#setCustomTargetSourceCreator(List)で設定します.

それでは実験を始めましょう.

こういう場合は,すでにあるものを使うより,自分で作ったほうがいいに決まってます.いやその,決してCommons Poolを学習するのが面倒だからではありません. あうあう.心より恥じる.

ということで,独自のTargetSourceを作ります.こういう場合は乱数です.何をするか考えるのが面倒になったら乱数なんです.乱数を返すようなTargetSource,その名もRandomTargetSourceを作ってみませう.

問題は,乱数をどのように扱うかです.CGLIBを使ったAOP Proxyではfinalなクラスは扱えないし,java.lang.reflect.Proxyを使ったAOP Proxyではinterfaceが必要なので,finalでかつinterfaceimplementsしていないIntegerとかは扱えません.Numberinterfaceなら... 無念だ.

しょうがないのでjava.util.Dateを使いましょう.

package study;
import java.util.Date;
import java.util.Random;
import org.springframework.aop.TargetSource;

public class RandomTargetSource implements TargetSource {
    private Random randomGenerator;
    public RandomTargetSource() {
        randomGenerator = new Random(System.currentTimeMillis());
    }
    public Object getTarget() throws Exception {
        return new Date(randomGenerator.nextLong());
    }
    public void releaseTarget(Object target) throws Exception {
    }
    public Class getTargetClass() {
        return Date.class;
    }
    public boolean isStatic() {
        return false;
    }
}

ちょろいもんです.えっへん.

今回はターゲットがDateなので,おなじみのFooは出番なしです.無念だ.

次に定義ファイル.<bean>要素以下のみ.

<bean name="interceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>

<bean id="date" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource">
        <bean class="study.RandomTargetSource"/>
    </property>
    <property name="interceptorNames">
        <value>interceptor</value>
    </property>
</bean>

今回はautoproxyではなく,ProxyFactoryBeanを使いました.そのtargetSourceプロパティにRandomTargetSourceを設定しています.Aspect(Interceptor)は,例によってDebugInterceptorです.心より恥じる.

で,実行用のクラス.今回の中心部はこんな感じ.

Date date = (Date) context.getBean("date");
System.out.println(date.getTime());
System.out.println(date.getTime());
System.out.println(date.getTime());

このように,同じインスタンスgetTime()を呼び出してプリントしています.本来Dateは不変オブジェクトなのですが...

実行!!!

Debug interceptor: count=1 invocation=[Invocation: method=[public long java.util.Date.getTime()] args=[Ljava.lang.Object;@f593af] target is of class java.util.Date]
Debug interceptor: next returned
8860736471533486537
Debug interceptor: count=2 invocation=[Invocation: method=[public long java.util.Date.getTime()] args=[Ljava.lang.Object;@f593af] target is of class java.util.Date]
Debug interceptor: next returned
5782795183581819694
Debug interceptor: count=3 invocation=[Invocation: method=[public long java.util.Date.getTime()] args=[Ljava.lang.Object;@f593af] target is of class java.util.Date]
Debug interceptor: next returned
9017716241053679149

今回はターゲットのメソッドの中でプリントしているわけではないので少々見にくいですが,getTime()の値が毎回異なっていることが確認できます.

それにしても,いくらお遊びとはいえDateの不変性をいぢるってのは... 心より恥じる.

[] JTA/JTS 15:17

現実逃避シリーズ第2弾(苦笑).

とめどもさんが日記でJTAとJTSについて混乱されているようなので,ちょっと書いてみるてすと(またしても弱気,ほとんどうろ覚えなので).

Java以前の分散トランザクション

正確には非Java環境での分散トランザクションですが,重要な仕様が二つあります.

  • X/Open*2 DTP(Distributed Transaction Processing)

この仕様は,80年代後半に作業が始まって,90年前後に最初の仕様がリリースされた(と記憶している)もので,簡単に言ってしまうとTUXEDO*3やEncina*4といったTPモニタのための仕様です(超乱暴).

初期のDTPのモデルはとても簡単(これが大事)で,主な構成要素は次の3つです.

    • Application(AP)
    • Transaction Manager(TM)
    • Resource Manager(RM)

このうちTMがTPモニタで,RMがDBMSとかMQSeries*5のようなMOM(Messaging Oriented Middleware)です.このような構成により,複数のRMに対してTMが2フェーズ・コミット(2PC)を司ってくれることになっています.

そして,上記の要素間で使用する以下のAPIが定められています.

TX
APとTM間のAPIで,トランザクションの開始・終了(commit/rollback)を行います.
XA
TMとRM間のAPIで,2PCを行います.

これらのAPIはいずれもC言語で規定されています.というか,XAに至っては関数ポインタの入った構造体なんていうしろものです.(^^;

DTPは後に拡張されて,cRM(Communication Resource Manager)という要素が追加され,(クライアント)APがcRMを通じて(サーバ)APを呼び出すためのAPI*6が定められたり,例えばTUXEDO上のAPからEncina上のAPを呼び出したい場合に,cRMを通じて複数のTMにまたがって2PCを行うためのXA+というAPI*7が定められたりしました.

さて,このDTPですが,TPモニタがそれほど広く普及したわけではないため,今となってはそれほど重要なものではないと個人的には思います.もっとも,XAだけは今も重要な存在です.


  • OMG CORBA OTS(Object Transaction Service)

X/Open DTPオブジェクト指向以前の分散トランザクション仕様です.それに対して,分散オブジェクト環境においても分散トランザクションが必要だということで,CORBAのCommon Object Serviceの一部として策定された仕様がOTSです.ということで,当然ながらCORBA上に構築された仕様です.

X/Open DTPはよく言えばシンプル,悪く言えば大雑把なモデルをベースにした仕様ですが,OTSはオブジェクト指向の専門家とトランザクションの専門化により練り上げられた,緻密なモデルをベースとしています.悪く言えば複雑ってことなんですが.

どれくらい複雑かを味わっていただくために(笑),その構成要素が持つべき主なinterfaceを列挙すると...

    • TransactionFactory
    • Control
    • Terminator
    • Resource
    • SubtransactionAwareResource
    • Synchronization
    • Cordinator
    • RecoveryCodinator

このうち,始めの3つがTXに,その後の2つがXAに相当するって感じでしょうか.

トランザクションを開始するのと,それを終了(commit/rollback)するのに別々のinterfaceが用意されていたりして,結構煩雑です.そのため,高水準のinterfaceとして,

    • Current

が用意されています.これはCORBAでいうところの擬似オブジェクト(Pseudo Object)で,CORBAで定められているもののCORBAオブジェクトではない(ObjectReferenceを持たない),手軽にサービスを使うためのオブジェクトです.

CORBA OTSは,X/Open DTPの経験をベースにしているだけに,よくできた仕様だと思います.しかし,前提となるCORBAが普及しなかったこともあり,メジャーな存在にはなれませんでした.無念だ.

特に,上記interface中のResourceがXAに相当するものなのですが,これを実装しているDBMS等がほとんどありません.私が知る限りでは,O2というフランス製のODBMSくらいです.考えてみれば,OracleやMQなんかがCORBAをサポートして分散トランザクションに参加するというのも,ちょっとなんだかなーって思いますよね.おっと,Oracleは中にVisiBroker持っているんでしたっけ.油断大敵?

そのため,OTSを実装した製品を使う場合でも,たいていはResourceとXAのアダプタを使ってRDBMSなどを使用することになります.


Java環境での分散トランザクション

ここからようやくJavaについて.

Javaにおいても,分散トランザクションが必要ということで,その仕様としてJTSとJTAの2つが定められています.

  • JTS(Java Transaction Service)

自分の記憶によれば,最初に定められたのはJTSです.これは,CORBA OTSを単純にJavaにマッピングしたものです.CORBAというのはプログラミング言語独立の仕様であり,前述のinterfaceはCORBA IDLで記述されています.乱暴に言うと,これをidl2javaしたものがJTSです.よって,膨大なJ2EE関連の仕様の中にありながら,JTS仕様書の本文はわずか10ページ程度しかありません.詳細はCORBA OTSを見ろってことなのです.

なお,とめどもさんが多少混乱されているようですが,JTSは実装ではなくて仕様です.CORBA IDLによるinterfaceをidl2javaして作られるのは,Javainterfaceです*8.CORBAでは実装を規定していませんから,それをJavaにマッピングしても実装は得られないのです.

そのJTSですが,当然ながらCORBA OTSと同じ問題を抱えています.それは,CORBAが前提だということです.いかにJ2SEの一部としてJava IDLというCORBA実装が提供されているとはいえ,CORBAが広く使われているわけではありませんし,そもそもJavaとして,CORBAに依存した分散トランザクションしか用意されていないのはいかにもまずいでしょう.


ということでJTAです.JTSがCORBA OTSのJavaマッピングなら,JTAはX/Open DTPJavaによる焼き直しという感じで,そっくりのモデルとAPIを持っています.その主なinterface

UserTransaction
TXに相当するinterface
XAResource
XAに相当するinterface

ちなみにXAResourceですが,名前もそこで定義されているメソッドもそのままXAな感じなのですが,これはあくまでJavainterfaceです.その実装がJNIでXAを呼び出しても構いませんが,そうしなければならないわけではありません.Pure JavaなRMはXAをサポートせずにXAResourceを提供するでしょうし,Type4のJDBCドライバが提供するXAResourceにしてもXAを使ったりはしないでしょう.XAはCで定義されているため,対応するCランタイムライブラリのバージョンの組み合わせなどがシビアだったりする場合もあり,XAResourceネイティブなRMが増えることに期待大です.

JTAは,CORBAのようなプラットフォームからは独立した仕様です.そのJTAを,JTS上の薄いラッパーとして実装することもできます.そのような実装の例として,SunのJ2EE Reference Implementationや,IBMWebSphere Application Serverがあります.これは,たまたまそういう風にJTAを実装したということで,JTAをネイティブに実装しても何の問題もありません.S2のJTAはネイティブな実装ですね.


ということで,JavaにはJTAとJTSという二つの分散トランザクション仕様があるわけですが,その主要な違いはCORBAに依存するか,しないかです.まれに,JTSは低水準でJTAは高水準という説明を見かけますが(っていうかJTAの仕様書にそういう図があるし),JTSにもCurrentというUserTransactionと同等の高水準interfaceがあるので,そのような説明は妥当ではないと個人的には思います.現在の位置付けとしては,アプリケーションが使用するのはJTAで,JTSはその水面下で使われることを想定しているっていうことはよく分かるのですけど.

そういえば「Seasarのからさわぎ」で,とめどもさんでしたっけ? JTSを実装をしたい!という方がいらっしゃいましたが,CORBAを前提とすることがほとんど考えられない現状では,その必要性は限りなく0に近いと考えてよいと思います.もちろん,趣味でということなら何の問題もありません.ぜひ実装してください!(^^;


JTAを使おう

JTAを使うと分散トランザクションを実現できるわけですが,分散トランザクションを使わない場合でもJTAを使う価値はあると思います.たいていのJTA実装は,トランザクションに参加するXAResourceが一つだけなら2フェーズコミットを行いません.S2のJTAもそうなっていますね.また,JDBCのcommit()/rollback()を直接呼び出すようなJTA実装もありでしょう.

ですから,たとえDB1つしか使わないという場合でも,JTAを使ってもいいのです.重要なのは,トランザクションはリソース(JDBCとかJMSとかJCAとかEJBとか)とは独立したものだということです.アプリケーションでJDBCのcommit()/rollback()を直接呼び出すようなことは,もうやめましょう.S2のようなコンテナを使うと,何も気にしなくてもJTAを使うことができます.というかJTAを使っていると意識することすらありませんね.素晴らしい.


2PCは慎重に

逆に,JTAが使えるからといって2PCを気軽に使うのは控えたほうがいいでしょう.2PCを使うと微妙な問題につきまとわれます.

  • パフォーマンス

2PCではコミットの際に,TMはそれぞれのRMと2回ずつやり取り(たいていは通信を伴う)を行います.アプリの処理に比べたら軽いものかもしれませんが,パフォーマンスの検証をしてから慎重に考えた方がいいでしょう.

  • コミットの順序

たまに悩ませてくれるコミット順.例えばOracleとMQで分散トランザクションを行っている場合.両方に対するPrepareを終えると,TMは一つ目のRMをコミットします.それがMQの方だとして,メッセージのPUTがコミットされたとしましょう.その時点で,他のアプリケーションはMQからメッセージをGETできます.そこでメッセージ中の項目をキーにOracleにアクセスすると,悲しいかなOracleはまだ先の2PCのコミットが終わっていなくて,あるべきデータがない,なんていうタイミングが発生する場合があります.どっちを先にコミットすべきかはアプリ次第だったりするので,なかなか悩ましいですー.

  • TMの障害

たいていの場合,DBMSなどのRMはトランザクションを一方的にロールバックできます.セッションが切れたとかタイムアウトしたとかで.2PCを使っている場合でも,Prepareの前までは同じです.しかし,RMがPrepareに対してコミットできるとTMに答えた場合,その後はTMからcommit/rollbackを指示されるまで,RMはそのトランザクションを自分の意志でどうこうすることはできなくなります.当然,そのトランザクションが持っているロックもかけっぱなしです.ところがその時,TMが吹っ飛んでいたりしたら? TMがトランザクションのログを残してくれていて,それにアクセスできる場合などはすみやかにリカバリできますが,TMのトランザクションログにアクセスできない障害の場合,トランザクションをコミットするかロールバックするかを誰かが判断して手作業で実施する必要があったりします.こういう事態に備えた運用をするのは結構たいへんじゃないでしょうか? いやその,2PC使っていてもそういう事態にちゃんと備えた運用手順を用意しているとは限らないのであうあう.心より恥じる.


あー,すっきりした.そろそろ現実に戻らねば... 無念だ.

[] お仕事スタイル 15:17

  • 黒のジップアップブルゾン(VIKTOR & ROLF Monsieur 03-04AW)
  • 黒とグレイのボーダーニット(BURBERRY Prosum 03SS)
  • 黒のベルベットパンツ(JIL SANDER 03-04AW)
  • 黒のショートブーツ(JIL SANDER 03-04AW)

こう天気や気温が不安定だと何着ていいのか困っちゃいます.

[][][] Inter 0 - 1 Marseille 09:55

(;_;)

これで後は来シーズンのCL出場権を目指すだけになってしもうた.

昨シーズンと違って楽しみのないこと... 無念だ.

*1nba.comの英文を見ただけなのでもしかしたら全然違うかも

*2:現在はThe Open Group

*3UNIX発祥の地であるAT&Tベル研で開発され,現在はBEAが所有.

*4:現在はIBMの一部であるTransarcが開発.

*5:言わずと知れたIBMのMOM.

*6:TUXEDO,Encina,CICSのAPIをそのまま追認という悲しい仕様ですが.

*7:例えに書いておいてなんですが,TUXEDOもEncinaもXA+は対応していないはず.NCRが開発(現在はBEAが所有)したTopEndくらいしか対応していないと思います.

*8:ヘルパクラスも大量に作られますが

zwfkzwfk 2004/04/15 17:21 うわーわざわざ歴史から説明してくださって分かりやすかったです。ありがとうございます。今日もいろいろ資料を見た結果、「JTSは低水準でJTAは高水準」だと解釈し始めたところでした。心より恥じる。ところで「JTSを実装をしたい」と言った方は私じゃないです。全然理解が足りてない現状の私にはそんな大それたこと言えない……(;´Д`A ```

koichikkoichik 2004/04/15 18:02 やはり人違いでしたか.たしか,はてなの人じゃないとのことだったのですが,お話の内容が似ていたので「あの人がとめどもさん?」なんて思っちゃいました.すみません.心より恥じる.それでその,とめどもさんとは会話しましたっけ?(^^; あの人数だったので,ちょっと誰が誰だか分からなかったり.無念だ.ぜひ川崎OFFで.

zwfkzwfk 2004/04/15 18:13 カラオケ屋で最初左後方にいたのが私です。机の配置上そっちの会話に入れず、あーあの方がkoichikさんなんだーと思ったのですが、突然話しかけても私のこと知ってるどうかわかんないし……と思って躊躇してしまいました。無念だ。

koichikkoichik 2004/04/15 18:41 歌チームの方だったんですね.(^^; とめどもさんはidでは分からなかったかもしれませんが(すみません),「挫折禁止」と言っていただければ即分かったはずです(笑).

SenbeiSenbei 2004/04/15 19:48 わ・た・し(〃 ̄ー ̄〃)

SenbeiSenbei 2004/04/15 20:13 私が話していた「分散トランザクション」とは、複数のRMが存在してると意味ではなく、複数のノードまたはクラスタが、それぞれTMを持っている場合の「分散されているトランザクションマネージャー」(複数のTMが存在している)状態のことを意味してました。口下手で、心より恥じる。その場合に、ひとつのトランザクションが複数のTMにまたがる場合には、通信マネジャーが存在しなければならなくて、たしか(ここからはうる覚え)JTAベースでは、無理でJTSベースで書かないとと。。。いうわけです。

koichikkoichik 2004/04/15 20:33 あ,あれはSenbeiさんでしたか.すみません,うろ覚えで.心より恥じる.なるほど,そういうことがやりたいのですか.ちなみにその場合でも,JTAでは無理ということはないです.JTAの仕様では何も規定されていないだけです(爆).つまり,TM独自の実装で連携する(同種のTMどうし)のであれば,表面的なAPIはJTAでもよいわけです.しかし,異なったTMの実装と連携するのであれば,その部分まで仕様化されているJTSということになるでしょう.ただし,その場合でも連携できるのはJTS実装どうしでしかないので,JTSが普及していない現状でどれだけ価値があるのかは? ましてや相互運用性の検証とか考えると... いずれにしても,JTS実装頑張ってください.(^^;

SenbeiSenbei 2004/04/16 10:28 もちろん、表面はJTAで、そいえば、IONAさんのOrbixのトランザクションって表面はJTAでなかJTSでしたね(当たり前か(笑))。とりあえず、自分のJTS同士がつながればOKなんです。まぁできたとしても、koichikさんが書かれているような問題点があるので悩ましいところであります。

SenbeiSenbei 2004/04/16 10:39 はてなへのトラックバック分からなかったので。http://senbei.cocolog-nifty.com/diary/2004/04/post_25.html から リンクはりました。