第8章 JNDI ネーミングサービス

ネーミングサービスはエンタープライズ Java アプリケーションにて重要な役割を果たしており、アプリケーションサーバーでオブジェクトやサービスの場所を検索する際に利用する中核インフラストラクチャーを提供しています。また、このサービスは、アプリケーションサーバーの外部にあるクライアントがアプリケーションサーバー内にあるサービスを検索するためのメカニズムでもあります。アプリケーションコードが JBoss Enterprise Application Platform インスタンス内部、外部のいずれであっても、このアプリケーションコードはqueue/IncomingOrders という名称のメッセージキューに接続するだけで、キューの設定詳細に関しては考慮する必要がありません。
クラスター環境では、ネーミングサービスは価値が高くなっています。サービスのクライアントは、どのマシンに常駐しているか把握する必要なしに、クラスターからProductCatalog セッション bean をルックアップできる必要があります。大規模なクラスター化サービス、ローカルリソース、あるいはアプリケーションコンポーネントのいずれかが必要かによって、JNDI ネーミングサービスは、コードが名前別にシステム内のオブジェクトを検索できるよう接着点としての機能を提供します。

8.1. JNDI の概要

JNDI は、Java 開発キットに同梱されている標準の Java API です。JNDI は、DNS、LDAP、Active Directory、RMI レジストリ、COS レジストリ、NIS、ファイルシステムなど、既存の各種ネーミングサービスに対して共通のインターフェースを提供しています。JNDI API はネーミングサービスのアクセスに使用するクライアント API と、ユーザーがネーミングサービスに対して JNDI 実装を作成できるようにするサービスプロバイダーインターフェース (SPI: service provider interface) とに論理的に分割されます。
SPI 層は、中核 JNDI クラスが共通の JNDI クライアントインターフェースを使いネーミングサービスを公開できるように、ネーミングサービスプロバイダーが実装しなければならない抽象化層で、ネーミングサービスに対する JNDI の実装は JNDI プロバイダーと呼んでいます。JBoss ネーミングは SPI クラスをベースとする JNDI 実装例になります。J2EE コンポーネント開発者には JNDI SPI は必要ないので注意してください。
主な JNDI API パッケージは、javax.naming パッケージで、この中にはインターフェースが5個、クラスが10個、例外が数個含まれています。主なクラス1つ (InitialContext) と、インターフェース2つ (Context および Name) が存在します。

8.1.1. 名前

名前の概念は JNDI では欠かせない重要なものとなります。ネーミングシステムにより、従うべき名前の構文が決定します。ユーザーはネーミングシステムの構文を使い、名前の文字列表現をコンポーネントに構文解析することができるようになります。名前はオブジェクトの検索にネーミングシステムで使用されます。最もシンプルに言い替えると、ネーミングシステムは単に一意名を持つオブジェクトの集合なのです。ネーミングシステムでオブジェクトを検索する場合、ネーミングシステムに名前を提示するとネーミングシステムはその名前を持つオブジェクトストアを返します。
例として、UNIX ファイルシステムの命名規則を見ていきます。各ファイルは、ファイルシステムの root を起点とした相対パスで命名され、それぞれのコンポーネントはスラッシュ ("/") で区切られます。このファイルのパスは左から右の順になります。たとえば、パス名 /usr/jboss/readme.txt は、ファイルシステムの root にある usr ディレクトリ配下のjboss ディレクトリ内にある readme.txt ファイルを指します。JBoss Enterprise Application Platform のネーミングは、Unix 形式の命名空間を命名規則として採用しています。
javax.naming.Name インターフェースは、汎用名をコンポーネントの順に表します。合成名 (複数の名前空間にわたる名前) の場合も、複合名の場合も (単一階層のネーミングシステム内で利用される名前) あります。名前のコンポーネントには番号がつけられます。コンポーネント N を持つ名前のインデックスは、0 から N 未満の範囲となっています。最も重要なコンポーネントは、インデックス 0 にあります。また、空の名前にはコンポーネントがありません。
合成名は、複数の名前空間にわたる一連のコンポーネント名です。合成名の例として、scp などの Unix コマンドで通常使われるホスト名とファイルの組み合わせなどでしょう。例えば、以下のコマンドは、localfile.txt を、 ahost.someorg.orgホストにある tmpディレクトリの remotefile.txt ファイルにコピーします。
scp localfile.txt ahost.someorg.org:/tmp/remotefile.txt
複合名は、階層名前空間から抽出されます。複合名の各コンポーネントは原子名 (atomic name) で、 それ以上小さなコンポーネントには分類できない文字列になります。Unix ファイルシステムのファイルパス名が複合名の一例です。ahost.someorg.org:/tmp/remotefile.txt は、DNS と Unix ファイルシステムの名前空間にわたる複合名となっています。また、複合名のコンポーネントは、ahost.someorg.org/tmp/remotefile.txtで、コンポーネントは、ネーミングシステムの名前空間からの文字列名となっています。コンポーネントが階層名前空間から来るものであれば、このコンポーネントは、javax.naming.CompoundName クラスを使って原子パーツにさらに構文解析することができます。JNDI API は、合成名の Nameインターフェースの実装として、javax.naming.CompositeName クラスを提供します。

8.1.2. コンテキスト

javax.naming.Context インターフェースは、ネーミングサービスとやりとりを行うにあたり主要なインターフェースとなっています。Context インターフェースは、名前とオブジェクトのバインディングセットを表します。全コンテキストは紐付けられた命名規則があり、それにより、コンテキストがjavax.naming.Name インスタンスで文字列名を解析する方法を決定します。名前とオブジェクトのバインディングを作成するには、Context のバインドメソッドを呼出し、名前とオブジェクトを引数として指定します。このオブジェクトは、あとでContextのロックアップメソッドを使った名前でリトリーブすることができます。通常、Context で、名前とオブジェクトのバインド、名前のアンバインド、名前とオブジェクトの全バインディングの一覧を取得する操作を行うことができます。Context にバインドするオブジェクトは、それ自体がContextタイプになり得ます。バインドされたContext オブジェクトは、バインドメソッドが呼び出されたところで Context のサブコンテキストとして参照されます。
例として、パス名が/usrで、Unix ファイルシステムではコンテキストとなるファイルディレクトリを見てみましょう。別のファイルディレクトリを起点に指定されたファイルディレクトリはサブコンテキスト (通常サブディレクトリと呼ばれる) となります。/usr/jbossとのパス名を持つファイルディレクトリは、usrのサブコンテキストであるjbossコンテキストを指定します。別の例では、orgなどのDNSドメインはコンテキストで、別のDNS ドメインを起点に指定されたDNS ドメインはサブコンテキストの別例となっています。DNSドメインjboss.orgでは、DNS名は右から左に解析されるため、DNS ドメインjbossorgのサブコンテキストとなります。

8.1.2.1. InitailContext を使ってコンテキストを取得

ネーミングサービスの操作はすべて Context インターフェースの実装で行われます。したがって、使おうとしているネーミングサービスの Context を取得する方法が必要になります。javax.naming.IntialContext クラスは Context インターフェースを実装するので、 ネーミングサービスとのやりとりを開始することができます。
InitialContext を作成する場合、この環境からのプロパティを使って初期化されます。JNDI は以下のソース2つからの値を順にマージすることで、各プロパティの値を決定します。
  • コンストラクターの環境パラメーターから最初に発生したプロパティ、(適切なプロパティに対して)アプレットパラメーターとシステムプロパティ
  • クラスパス上にある全 jndi.properties リソースファイル
上記の 2 ソース両方にある各プロパティに関して、プロパティの値が次のように確定されます。プロパティが JNDI ファクトリ一覧を指定する標準の JNDI プロパティのいずれかの場合、 すべての値がコロンで区切られた単一リストに連結されます。その他のプロパティは最初に見付かった値のみが使用されます。JNDI 環境プロパティを指定する方法としては、 jndi.properties ファイルを使う方法をお勧めします。このファイルによりコードが JNDI プロバイダー固有の情報を外部に配置することができるようになるため、JNDI プロバイダーを変更してもコードの変更や再コンパイルを必要としません。
InitialContext クラスが内部で使用するContext 実装は、ランタイム時に決定されます。デフォルトポリシーは、javax.naming.spi.InitialContextFactory 実装のクラス名を含む環境プロパティ java.naming.factory.initialを使います。ご利用中のネーミングサービスプロバイダーからInitialContextFactory クラス名を取得します。
例8.1「jndi.properties ファイルの例」 で、クライアントアプリケーションがポート 1099 のローカルホストで稼働するJBossNS サービスに接続する際に利用するサンプルのjndi.properties ファイルを提供しています。クライアントアプリケーションは、アプリケーションクラスパスにあるjndi.properties ファイルが必要となるでしょう。これらは、JBossNS JNDI 実装が必要とするプロパティです。その他のJNDI プロバイダーには、別のプロパティと値があります。

例8.1 jndi.properties ファイルの例

### JBossNS properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://localhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces