2008年12月10日(水) [長年日記]
- Apach Axis2を使っている。色々わからない。
■ [java][db][web][howto] Tomcat 6でのJDBC Data Sourceの設定
Tomcatは3年に1度くらいしか使わないので全然覚えない。使う度にバージョンが変わっているし。今はバージョン6.0.18。
JDBC Data Sourceの設定方法を調べたのでメモしておく。
JDBC Data Sourceとは
javax.sqlパッケージ にあるDataSourceインタフェースのことだと思う。このインタフェースの使い方は JDBC API 入門 などに説明がある。
JDBCを使うときには java.sql.Connection を利用するが、これのインスタンスを取得するのにDataSourceオブジェクトが必要になる。ウェブアプリケーションでは、アプリケーションコンテナにDataSourceオブジェクトを作らせそれをJNDI APIを使って取得するように実装することが推奨されているみたい。
コネクションプーリング機能を持ったDataSourceオブジェクトをコンテナに作らせれば、ウェブアプリケーション側では意識せずともプーリングされたConnectionオブジェクトを利用できる。
Tomcatでの設定方法
Tomcatにはコネクションプーリング機能を持ったDataSourceオブジェクトを作る機能があるようなので、それを利用したい。設定方法は JNDI Resources HOW-TO に書いてある。この後、ここに書いてあるのと同じことをメモする。なお、同じマニュアルのTomcat 5版の日本語訳が Ja-Jakarta にありありがたく参照したが、Resourceの設定方法がTomcat 5.5以降で変わったようで、それに気付かず長時間はまった。
やることは次の3つ。
- JDBCドライバの配置
- 使用するDBMS用のJDBCドライバをTomcatが読み込める場所に配置する。$CATALINA_HOME/lib に配置するのが簡単だと書いてあった。
- Context設定ファイルの配置
- TomcatがDataSourceインスタンスを作成してそれをJNDIに登録するための情報を、Contextという形で設定するみたい。
- Contextの設定方法は The Context Container というページに書いてあった。Contextを記述したXMLファイルを作ればいいみたい。
- XMLファイルの置き場所は色々あるようで、その中の /META-INF/context.xml に置くのがよさそうに思ってそうしてみたが、ここに置いても読み込んでくれなかった。META-INFの場所をおれが勘違いしているのかなあ。
- $CATALINA_BASE/conf/[enginename]/[hostname]/[コンテキストパス].xml に置くのでもよいと書いてあるのでそうしたら動いた。コンテキストパスというのがよく分かっていないけど、URLに現れるアプリケーション名のことだと思っている。
- XMLファイルの内容は次のような感じ。この書き方がTomcat 5までとは違う。Tomcatは Commons DBCP を使っているようなので、設定パラメータについては Commons DBCPのマニュアル も参考になりそう。
<Context> <Resource name="jdbc/oreore" auth="Container" type="javax.sql.DataSource" username="user" password="pswd" driverClassName="org.apache.derby.jdbc.ClientDriver" url="jdbc:derby://localhost/oreoreDB" maxActive="8" maxIdle="4"/> </Context>
- アプリケーションのweb.xmlの編集
- 上で設定したDataSourceをアプリケーションからJNDI APIで参照できるようにするために、web.xmlに次のような設定を追加する必要があるみたい。web-app要素内に書く。authの所をよく分かっていないなあ。本当に必要なのだろうか。
<resource-ref> <res-ref-name>jdbc/oreore</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
アプリケーションからのDataSourceの取得方法
上のように設定すると、アプリケーションのコードで
Context ictx = new InitialContext(); DataSource dataSource = (DataSource)ictx.lookup("java:comp/env/jdbc/oreore");
のようにJNDIを使ってDataSourceオブジェクトを取得できる。
「jdbc/oreore」を3箇所に分散して書かないといけない所がひどい。
■ [java][db] DataSourceはスレッドセーフなのか
スレッドセーフじゃないと困ると思うが、明記したドキュメントが見当たらない。
検索したら @IT会議室 がヒットし、そこに For Driver Writers の「A.1.6 Support Multithreading」に記述があるという発言があった。確かに
All operations on java.sql and javax.sql objects are required to be multithread safe.
で始まっている。それ以降はちゃんと読んでないけど。
これを信じてもいいんだろうか。Connectionのスレッド安全性は実装依存というのを以前にどこかで読んだ記憶があるんだよな。念のため自分で排他制御すべきかなあ。
■ [java][web] Axis2でのサービスオブジェクトのライフサイクル
Axis2にて、サービスクラスのインスタンスはどのように管理されるのか。
コンストラクタでログを出力するようにして試したところ、デフォルトでは要求を受信する度に新しいインスタンスが作られるようだった。
Axis2 Configuration Guide のService Configuration節に、
scope: (Optional Attribute) The time period during which runtime information of the deployed services will be available. Scope is of several types- "Application", "SOAPSession", "TransportSession", "Request". The default value (if you don't enter any value) will be "Request"
と書いてあった。デフォルト値が Request だからか。
それならと services.xml に「scope="Application"」を追記して試してみたが、やっぱり要求を受信する度に新しいインスタンスが作られる。なんでや。
調べると「scope="application"」のように全部小文字の例があったのでそれを試したら、Tomcatの起動時にインスタンスが生成され、新しい要求を受信しても新しいインスタンスは作られないようになった。ということは、ドキュメントの説明がおかしい。あっ、それともXMLってクォーテーション内も小文字だけ使うんだったっけか。いやいや、そんなことないよなあ。
■ [java][web] Axis2のライフサイクル管理
上で引用したService Configuration節に、
class: (Optional attribute) The full qualified name of the service lifecycle implementation class. ServiceLifeCycle class is useful when you want to do some tasks when the system starts and when it shuts down.
なんてことも書かれている。この説明だけでは何の事だか分からない。属性名がclassってのはひどい。
検索すると、Axis2 services : LifeCycle Vs ServiceLifeCycle というページがあった。ServiceLifeCycleとLifecycleという2つのインタフェースがあるのか。LifeCycleなのかLifecycleなのかはネイディブの人でも悩むのかね。
試した結果をメモ。
org.apache.axis2.engine.ServiceLifeCycle
このインタフェースを実装したクラスを作り、そのクラス名をservices.xml内のclass属性に指定しておくと、
- Axisエンジン起動時にインスタンスが作られ、startUp(ConfigurationContext configctx, AxisService service) が呼ばれる。
- Axisエンジン終了時に shutDown(ConfigurationContext configctx, AxisService service) が呼ばれる。
となるみたい。引数で渡されるオブジェクトをどう使えばいいのかがよく分からない。
Axis2のアーカイブ内のsamplesディレクトリに servicelifecycle というサンプルがあって、そこでこのインタフェースを使っていた。
org.apache.axis2.service.Lifecycle
サービスクラスにてこのインタフェースを実装しておくと、
- サービスクラスのインスタンスが作られたときに init(ServiceContext context) が呼ばれる。
- サービスクラスのインスタンスが用済みになったときに destroy(ServiceContext context) が呼ばれる。いつ用済みになるかは上に書いた「scope」の設定で決まる。
となるのかと思ったが、そうならないケースもあった。うーん。
scopeをapplicationにしているときは上のようになったが、requestにしたときはdestroy()が呼ばれない。finalize()は呼ばれているのにdestroy()は呼ばれない。なんでや。
■ やること
請書スポンジ- Microsoft Update
- 換金