- PR -

JNDIでデータソースを取得する(RMI & Tomcat)

1
投稿者投稿内容
Jumpin'' Jack Flash
大ベテラン
会議室デビュー日: 2006/01/24
投稿数: 198
投稿日時: 2006-12-13 17:02

"JNDIでデータソースを取得する"あたりの話です。
3つほど質問がございます。

できていること:

・RMIからデータソースを取得

-- RMIサーバプログラムを実行し、

-- jndi.properties --
java.naming.factory.initial=com.sun.jndi.rmi.registry.RegistryContextFactory
java.naming.provider.url=rmi://localhost:1099/
-- .java ------------
Context context = new InitialContext();
DataSource dataSource = (DataSource)context.lookup("dataSource1");
---------------------

・Tomcat上でデータソースを取得

-- server.xml, web.xmlを編集し、

-- Tomcat上で -------

-- .java ------------
Context context = new InitialContext();
DataSource dataSource = (DataSource)context.lookup("java:comp/env/dataSource1");
---------------------

質問1:

Tomcatのデータソースを取得する際、"java:comp/env/"を付けないで、
RMIからのデータソースを取得する際と同様に"dataSource1"という名前で
取得できるようにできませんか?
これが可能になると、クライアントはデータソースサーバーを意識することが
なくなります。

質問2:

RMIレジストリにバインドする際、データソース名を"jdbc/dataSource1"のように
"/"を含めると、"/"以降を無視し、"jdbc"という名前でバインドされるようです。
データソース名として一般的な、"jdbc/dataSource1"という名前で登録することは
できますか?

質問3:

Tomcat上のWebアプリケーションからではなく、コンソールアプリケーションから、
Tomcat上のデータソースを取得することはできませんか?
過去ログ>
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=27470&forum=12&7
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=17003&forum=12&6
このあたりを見た限り、できないようですね。


以上、よろしくお願いいたします。
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2006-12-13 17:48
"これが可能になると、クライアントはデータソースサーバーを意識することが なくなります。"とはどいういう意味でしょう?

"データソースサーバーを意識すること"というのが、データソース名をハードコードしなくて済むこと、を指しているのであれば "java:comp/env/dataSource1" と書く方がむしろ "データソースサーバーを意識すること"がない書式ではないでしょうか。
resource-ref で間接的に参照するわけですから。
Jumpin'' Jack Flash
大ベテラン
会議室デビュー日: 2006/01/24
投稿数: 198
投稿日時: 2006-12-13 18:11
引用:

インギさんの書き込み (2006-12-13 17:48) より:
"これが可能になると、クライアントはデータソースサーバーを意識することが なくなります。"とはどいういう意味でしょう?



すいません。書き方が悪かったです。

クライアント --[dataSource1]--> RMIデータソース
クライアント --[java:comp/env/dataSource1]--> Tomcatデータソース
※[]内は、lookupに使う[データソース名]

というように、データソースサーバーによって、別の名前でlookup
しなければなりません。すなわち、"クライアントはデータソースサーバー
を意識している"状態だと言いたかったわけです。

例えば、(まだやってないけど)
クライアント --[dataSource1]--> IIOPデータソース
なんてこともできて、これは、データソース名は変わらず、
jndi.propertiesの設定変更だけで参照先サーバーを変更できます。

まぁ、jndi.propertiesもクライアント側の持ち物(設定ファイル)なので、
また、"データソースサーバー"と"データベースサーバー"とが混同して
しまいますので
"クライアントプログラムはJNDIデータソースサーバーを意識している"と
言った方が正確でしょうか。
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-12-13 22:32
引用:

Jumpin' Jack Flashさんの書き込み (2006-12-13 17:02) より:
Tomcatのデータソースを取得する際、"java:comp/env/"を付けないで、
RMIからのデータソースを取得する際と同様に"dataSource1"という名前で
取得できるようにできませんか?



Tomcatの構成がjava:comp/envを前提としているので無理でしょう。
まともなJNDIを実装しているJava EEコンテナでは"dataSource1"を
"java:comp/env/jdbc/dataSource1"にマッピングする使い方をします。

#この際に前者を「物理JNDI名」、後者を「論理JNDI名」と呼ぶのが
#一般的なようです(仕様を見たわけではないので定かではありません)。

引用:

RMIレジストリにバインドする際、データソース名を"jdbc/dataSource1"のように
"/"を含めると、"/"以降を無視し、"jdbc"という名前でバインドされるようです。
データソース名として一般的な、"jdbc/dataSource1"という名前で登録することは
できますか?



Context#createSubcontext()を使いましょう。

引用:

Tomcat上のWebアプリケーションからではなく、コンソールアプリケーションから、
Tomcat上のデータソースを取得することはできませんか?



無理でしょうねぇ。
TomcatのJNDI実装はかなり限定的なので。
Jumpin'' Jack Flash
大ベテラン
会議室デビュー日: 2006/01/24
投稿数: 198
投稿日時: 2006-12-14 18:15
回答1:

引用:

Tomcatの構成がjava:comp/envを前提としているので無理でしょう。
まともなJNDIを実装しているJava EEコンテナでは"dataSource1"を
"java:comp/env/jdbc/dataSource1"にマッピングする使い方をします。



---------------------
Context context = new InitialContext();
DataSource dataSource = (DataSource)context.lookup("java:comp/env/dataSource1");
---------------------
↑これは、↓このように書けるようですね。
---------------------
Context context = new InitialContext();
context = (Context)context.lookup("java:comp/env");
DataSource dataSource = (DataSource)context.lookup("dataSource1");
---------------------

---------------------
context = context.lookup("java:comp/env");
---------------------
↑この部分を、設定ファイルに定義して、
-- jndi.properties --
java.naming.context.prefix=java:comp/env
---------------------
(jndi.properties に入れていいものかは賛否ありそうですが)

context.getEnvironment().get("java.naming.context.prefix");
で、取得できそうですね。

↓こうなりました。
---------------------
Context context = new InitialContext();
String prefix = (String)context.getEnvironment().get("java.naming.context.prefix");
if (prefix != null) context = (Context)context.lookup(prefix);
DataSource dataSource = (DataSource)context.lookup("dataSource1");
---------------------

解決。


回答2:

引用:

Context#createSubcontext()を使いましょう。



javax.naming.OperationNotSupportedException がスローされました。
どうも、RMIレジストリは階層構造をサポートしていないようです。

で、考えました。

データソース名をエスケープ(jdbc/dataSource1 → jdbc%2FdataSource1)して、
階層構造のコンテキストにアクセスしているように見せかけようと思いました。
エスケープは、URLEncoder.encode()で簡単にできるのですが、ContextFactory と Context の実装がやや難解でした。

まず、com.sun.jndi.rmi.registry.RegistryContextFactory を継承したクラスを作り、
getInitialContext() と getObjectInstance() をオーバーライドしました。

そして、それぞれのメソッドの戻り値(com.sun.jndi.rmi.registry.RegistryContext)をラップするクラスを作り、
そのクラスの参照を戻すようにしました。

で、そのラッパクラスに Context インターフェースを実装し、データソース名(コンテキスト名)をエスケープして
ラップした com.sun.jndi.rmi.registry.RegistryContext オブジェクトの同メソッドを呼ぶようにしました。

あとは、jndi.properties にその ContextFactoryクラスを指定すれば思うように動いてくれました。

解決。


回答3:

引用:

無理でしょうねぇ。
TomcatのJNDI実装はかなり限定的なので。



${TOMCAT_HOME}/common/lib に、
naming-factory.jar とか naming-factory-dbcp.jar とか naming-resources.jar とかあるようですので、
ハックしてみます。


いろいろ教えてくださって、ありがとうございました。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2006-12-14 19:35
話がそれて申し訳ないですが・・・

DIっぽいアプローチですが、
DataSourceFactoryってインターフェイスを作って、
DataSourceFactory経由でデータソース取得っていうのはどうでしょう。

JNDIのリソース名とか実装クラス名は設定ファイルで持つようにする感じです。

環境が変わっても設定ファイルや実装クラスを変えるだけで対処できますし、
別プログラムでデータソースを使いたい時は、
直接データソースを生成してDataSourceFactory経由で返すようにも可能です。
Jumpin'' Jack Flash
大ベテラン
会議室デビュー日: 2006/01/24
投稿数: 198
投稿日時: 2006-12-15 10:27
かつのり様

アイディアを提供していただき、ありがとうございます。

おっしゃるように、本来は、ちゃんと設計すべきだと思うのですが、
ずぼらなもので、"最小限のコストで実装するとこうなりました"という
アプローチを提示させていただきました。

また、DataSourceに特化することなく、JNDIの汎用性をそのまま踏襲
するという意味において、そこそこいけてるんじゃないかとも思っています。

ただ、その上位にDataSourceFactoryっていうのは、アリだと思うので、
かなり参考になりました。うれしいご指摘です。

貴重なご意見ありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)