[先頭の項目] [前の項目] [次の項目] [最後の項目]

1 はじめに

JNDI SPI は、開発者がネームサービスとディレクトリサービスのさまざまな「サービスプロバイダ」を記述し、それらに対応するサービスを、JNDI API を使用するアプリケーションからアクセスできるよう準備するための手段です。サービスプロバイダは、共同で JNDI API の要求を満たすモジュールのセットです。また、JNDI では複数の名前空間にまたがる名前 (複数) の使用が許されるので、サービスプロバイダの実装で 1 つの操作を行うために、別のサービスプロバイダとの相互作用が必要な場合があります。SPI には、複数の異なるプロバイダ実装が共同でクライアントの JNDI 操作を行えるようにするためのメソッドがあります。

このドキュメントでは、SPI のコンポーネントについて解説し、開発者が JNDI のためのサービスプロバイダを作成する方法について説明します。読者は JNDI API ドキュメントの内容に習熟しているものと仮定します。

サービスプロバイダの開発者は、必ず JNDI API ドキュメントの「セキュリティについて」を読んでください。JNDI を使用する開発者、特にサービスプロバイダの開発者が考慮すべき重要な内容が含まれています。

1.1 このドキュメントの概要

JNDI API の下に位置する実装には複数のタイプがあります。サービスプロバイダには最低 1 つの「コンテキスト実装」が含まれます。コンテキスト実装は、Context インタフェースまたはそのサブインタフェース (DirContextEventContextLdapContext など) を実装します。実装の複雑さは、主に背後のサービスの複雑さに依存し、次にその実装がサポートする JNDI の機能の数に依存します。第 2 章では、コンテキスト実装の構築の詳細について説明します。

コンテキスト実装には、さまざまな方法でアクセスできます。もっとも一般的な方法は、初期コンテキストからアクセスする方法です。第 3 章では、初期コンテキストからコンテキスト実装にアクセスする 2 つの方法を説明します。初期コンテキストファクトリを通じて行う方法と、URL コンテキストファクトリを通じて行う方法です。

JNDI アーキテクチャは、コンテキスト実装の動作を強化するために使用できるコンポーネントまたは実装を定義します。これにより、ユーザとアプリケーションは実装をカスタマイズすることができます。これらのコンポーネントは「ファクトリ」を通じてサポートされます。JNDI には 3 種類のファクトリが定義され、それらを利用するための SPI メソッドがあります。これらのファクトリについては、第 4 章に説明されています。

1.2 インタフェースの概要1

JNDI SPI は javax.naming.spi パッケージに含まれています。ここでは、SPI の概要について説明します。SPI の詳細は、対応する javadoc ドキュメントを参照してください。

javax.naming.ldap パッケージ。このグラフィックの説明は API のドキュメント参照してください。.

1.2.1 NamingManager と DirectoryManager

NamingManager クラスにはプロバイダ関連の操作を行う static メソッドが含まれます。たとえば、Reference を使ってオブジェクトのインスタンスを生成するメソッドや、java.naming.factory.initial プロパティを使って初期コンテキストのインスタンスを取得するメソッド、ObjectFactoryBuilderInitialContextFactoryBuilder をインストールするメソッドなどがあります。DirectoryManager クラスには、DirContext 関連の操作のための類似した static メソッドが含まれます。

1.2.2 初期コンテキスト

InitialContextFactory は、初期コンテキストのインスタンスを生成するためのインタフェースです。詳細は、節 3.1 を参照してください。

InitialContextFactoryBuilder は、InitialContextFactory のインスタンスを生成するためのインタフェースです。詳細は、節 3.3 を参照してください。

1.2.3 オブジェクトファクトリ

ObjectFactory は、名前空間に保存されている情報を使ったオブジェクト生成をサポートするためのインタフェースです。DirObjectFactoryObjectFactory のサブインタフェースです。DirContext インタフェースを実装するコンテキスト実装により使用されます。詳細は、節 4.1 を参照してください。

ObjectFactoryBuilder は、オブジェクトファクトリを生成するためのインタフェースです。詳細は、節 4.1.4 を参照してください。

1.2.4 状態ファクトリ

StateFactory は、ネームサービスまたはディレクトリサービスでサポートされる保存可能な形式へのオブジェクトの変換をサポートするためのインタフェースです。DirStateFactoryStateFactory のサブインタフェースで、DirContext インタフェースを実装するコンテキスト実装により使用されます。DirStateFactory.Result は、DirStateFactory.getStateToBind() から返される、java.lang.ObjectAttributes のペアを保持するためのクラスです。詳細は、節 4.2 を参照してください。

1.2.5 連合のサポート

Resolver インタフェースは、Context への拡張インタフェースをサポートするための連合に参加できるようにプロバイダを実装するためのメソッドを定義します。詳細は、コンテキストのサブインタフェースを通じた解釈処理を参照してください。

ResolveResult は、Resolver.resolveToClass() の呼び出しの戻り値です。解釈に成功したオブジェクトと、未解釈の名前が含まれます。

2 コンテキスト実装の構築

サービスプロバイダ構築の基本作業の 1 つは、Context インタフェースまたはそのサブインタフェースのどれかを実装するクラスを定義することです。このクラスは「コンテキスト実装」と呼ばれます。コンテキスト実装の開発には、次のガイドラインを参照してください。

2.1 パラメータの所有権

一般に、Context インタフェース (またはサブインタフェース) 内のメソッドおよび、NamingManager または DirectoryManager ユーティリティメソッドにパラメータとして渡されるオブジェクトは、呼び出し側が所有します。多くの場合、パラメータは最終的にコンテキスト実装に到達します。呼び出し側がオブジェクトを所有しているため、コンテキスト実装がそのオブジェクトを修正することは禁じられています。さらに、コンテキスト実装がそのオブジェクトへのポインタを保持できるのは操作の間のみで、それ以降のポインタの保持は許可されません。コンテキスト実装がパラメータに含まれる情報を保持する必要がある場合は、そのコピーを保持するものとします。

パラメータ所有権の目的から、コンテキストインスタンス上の操作は、「その操作によって生成されるすべての照会が続行している間」、または「列挙がまだ使用されているときに操作が NamingEnumeration を返した場合」は、まだ終了していないと見なされます。

2.2 再入可能性

コンテキストのインスタンスは再入可能である必要はありません。同じコンテキストインスタンスを同時にアクセスする必要のある 2 つのスレッドは、互いに同期して必要なロックを行うものとします。

しかし、異なるコンテキストインスタンスは、複数スレッドの同時アクセスに対して安全でなければなりません。つまり、それぞれのコンテキストインスタンスに同時に操作を行う 2 つのスレッドは、互いにアクセスを同期する必要があってはなりません。たとえば、2 つのコンテキストが同じリソース (同じ接続など) を共有している場合でも、2 つの別々のスレッドが明示的な同期を行わなくてもそれぞれのコンテキストでの処理が可能 (かつ安全) でなければなりません。

並行性制御の目的から、あるコンテキストのインスタンス上の操作は、「その操作によって生成されるすべての照会が続行している間」、または「列挙がまだ使用されているときに操作が NamingEnumeration を返した場合」は、まだ終了していないと見なされます。

2.3 基本サポート - コンテキストインタフェースの実装

コンテキスト実装は、その実装がサポートする Context インタフェースまたはそのサブインタフェース内の個々のメソッドのための実装を定義します。

メソッドがサポートされていない場合は、OperationNotSupportedException をスローするものとします。

Context インタフェースまたはサブインタフェース内のメソッドのうち、名前の引数を (String または Name として) 受け取るメソッドに対して、空白の名前は現在のコンテキストを表します。たとえば、lookup() に空白の名前が与えられた場合は、現在のコンテキストの新しいインスタンスを返すことを意味します。list() に空白の名前が与えられた場合は、現在のコンテキスト内の名前を列挙することを意味します。getAttributes() に空白の名前が与えられた場合は、そのコンテキストに関連付けられた属性を取得することを意味します。

付録 A には、フラットなメモリ内名前空間を実装するコンテキスト実装の例が示されています。

2.4 オブジェクトのサポート

JNDI では、Java アプリケーションのプログラマにとって自然で直観的な Context (およびそのサブインタフェース) の実装が、プロバイダにより提供されることが奨励されます。たとえば、名前空間上でプリンタ名をルックアップするとき、プログラマは当然、処理対象のプリンタオブジェクトが戻されることを予期します。

Context ctx = new InitialContext(); 	
Printer prt = (Printer)ctx.lookup(somePrinterName);
prt.print(someStreamOfData);

同様に、アプリケーションのオブジェクトを背後のサービスに保存するとき、アプリケーションが背後のデータ表現について知る必要がない場合には、自然で直観的な実装がもっとも可搬性があり便利です。

しかし、背後のディレクトリサービスまたはネームサービスにバインドされているのは通常、Java プログラミング言語のオブジェクトではなく、実際のオブジェクトを検出またはアクセスするために使用できる参照情報です。このケースは多く、特に既存のインストール済みマシンにおけるサービスをアクセスおよび共有する Java アプリケーションでは、ごく一般的です。参照は事実上、実際のオブジェクトへの「ポインタ」の役割を果たします。プリンタの例では、実際にバインドされているのはプリンタへのアクセス方法に関する情報 (プロトコルタイプ、サーバアドレスなど) などです。アプリケーションの開発者がこの便利なモデルを使用できるようにするには、背後のサービスから Java プログラミング言語の適切なオブジェクトへのデータの変換をコンテキスト実装が行う必要があります。

この目標を達成するには、さまざまな方法があります。あるコンテキスト実装が、1 つのディレクトリが返せるオブジェクトのすべての実装クラスへのアクセスを持つ場合があります。 また、あるコンテキス実装が、オブジェクトの実装クラスを探すための特別なクラスローダを持つ場合もあります。JNDI には、参照を表現するための標準的な方法として Reference クラスがあります。アプリケーションとコンテキスト実装では、独力で個別の機構を創作するよりも、このクラスを使用することが奨励されます。ただしこれによって、同じ目的を達成するためにコンテキスト実装が独自の機構を使うことができなくなるということはありません。

JNDI には、コンテキスト実装が Java プログラミング言語内のオブジェクトを背後のサービスでの形式に依存しない方法で読み込んだり保存したりするためのユーティリティがあります。ここでは、これらのユーティリティについて説明します。これらのユーティリティは、「オブジェクトファクトリ」および「状態ファクトリ」と呼ばれる、実際の変換を行うコンポーネントと相互作用します。これらのファクトリについては、第 4 章に説明されています。

2.4.1 オブジェクトの読み込み

JNDI には、背後のサービスから読み込んだデータを Java プログラミング言語のオブジェクトに変換するためにコンテキスト実装が使用すべき、次のメソッドがあります。

Object NamingManager.getObjectInstance(Object refInfo,
                                       Name name,
                                       Context nameCtx, 
                                       Hashtable env)
	throws Exception;
Object DirectoryManager.getObjectInstance(Object refInfo,
                                          Name name,
                                          Context nameCtx,
                                          Hashtable env,
                                          Attributes attrs)
	throws Exception;

refInfo は、基本的なサービスから読み込まれる (オブジェクトを表現する) データです。 name はオブジェクトの名前、nameCtxname が解釈されるコンテキストです。namenameCtx を対で使用すると、オブジェクトに関する情報を refInfo から得られるよりも多く取得することができます。env は、getObjectInstance() の呼び出し元のコンテキストの環境です。attrs は、オブジェクトについて、ディレクトリから読み込まれる属性のコレクションで、通常は refInfo を取得するために使用されるものと同じ要求です。完全なコレクションが要求されなかった場合は、属性の完全なコレクションでないことがあります。

Context インタフェースを実装するコンテキスト実装では NamingManager クラス内のメソッドを使用し、DirContext インタフェースを実装するコンテキスト実装では DirectoryManager クラス内のメソッドを使用するものとします。

次のメソッドに返すオブジェクトを構築するときは、コンテキスト実装は getObjectInstance() を呼び出すものとします。 または、バウンド情報からオブジェクトを生成する独自の機構をコンテキスト内で使いたい場合は、その機構を呼び出すものとします (文字列のオーバーロードは示されない)。

	javax.naming.Context.lookup(Name name)
	javax.naming.Context.lookupLink(Name name)
	javax.naming.Binding.getObject()
	javax.naming.directory.SearchResult.getObject()

BindingSearchResult については、コンテキスト実装は、getObjectInstance() の呼び出し結果のオブジェクトまたは、それに相当するものをコンストラクタに渡すものとします。 または、BindingSearchResult のデフォルト実装をオーバーライドして、getObject() の実装が呼び出し元に戻る前に、getObjectInstance() または、それに相当するものを呼び出すようにします。

次に例を示します。プリンタは Reference を使って名前空間内に表現されています。プリンタの Reference を実際の Printer オブジェクトに変換するために、コンテキスト実装は NamingManager.getObjectInstance() メソッドを使用します。この方法では、背後のサービスはプリンタに関する特定の情報を知る必要がありません。

Object lookup(Name name) {	
	...
	Reference ref = <some printer reference looked up from naming service>; 	
	return NamingManager.getObjectInstance(ref, name, this, env);
}

次の例では、プリンタは属性のコレクションとしてディレクトリ内に表現されているものとします。プリンタのディレクトリエントリを実際の Printer オブジェクトに変換するために、コンテキスト実装は DirectoryManager.getObjectInstance() を使用します。

Object lookup(Name name) {	
	...
	Attributes attrs = <read attributes from directory>;
	Reference ref = <construct reference from attributes>;	
	return DirectoryManager.getObjectInstance(ref, name, this, 
		env, attrs);
}

2.4.2 オブジェクトの保存

JNDI には、オブジェクトを背後のサービスに保存する前に変換するための、次のメソッドがあります。

Object NamingManager.getStateToBind(
                                    Object obj,
                                    Name name,
                                    Context nameCtx, 
                                    Hashtable env)
	throws NamingException;
DirStateFactory.Result DirectoryManager.getStateToBind(
                                    Object obj,
                                    Name name,
                                    Context nameCtx,
                                    Hashtable env,
                                    Attributes attrs)
	throws NamingException;

obj は、基本のサービスに保存されるオブジェクトです。name はオブジェクトの名前、nameCtxname が解釈されるコンテキストです。namenameCtx を対で使用すると、オブジェクトに関する情報が obj から得られるよりも多く取得することができます。env は、getStateToBind() の呼び出し元のコンテキストの環境です。attrs は属性のコレクションで、オブジェクトにバインドされます。DirStateFactory.Result はオブジェクトおよびその属性のコレクションを含むクラスです。

Context インタフェースを実装するコンテキスト実装では NamingManager クラス内のメソッドを使用し、DirContext インタフェースを実装するコンテキスト実装では DirectoryManager クラス内のメソッドを使用するものとします。

アプリケーションから与えられたオブジェクトを保存するときは、その前にコンテキスト実装は getStateToBind() を呼び出すものとします。 または、バウンド情報からオブジェクトを生成する独自の機構をコンテキスト内で使いたい場合は、その機構を呼び出すものとします (文字列のオーバーロードは示されない)。

javax.naming.Context.bind(Name name, Object o)
javax.naming.Context.rebind(Name name, Object o)
javax.naming.DirContext.bind(Name name, Object o, Attributes attrs)
javax.naming.DirContext.rebind(Name name, Object o, Attributes attrs)

次に、Context 実装による Context.bind のサポートの例を示します。

// First do transformation
obj = NamingManager.getStateToBind(obj, name, ctx, env);

// Check for Referenceable
if (obj instanceof Referenceable) {
    obj = ((Referenceable)obj).getReference();
}

if (obj instanceof Reference) {
	// store as ref
} else if (obj instanceof Serializable) {
	// serialize
} else {
	... 
}

次に、DirContext 実装による DirContext.bind のサポートの例を示します。

// First do transformation
DirStateFactory.Result res = DirectoryManager.getStateToBind(
	obj, name, ctx, env, inAttrs);

obj = res.getObject();
Attributes outAttrs = res.getAttributes();

// Check for Referenceable
if (obj instanceof Referenceable) {
	obj = ((Referenceable)obj).getReference();
}
if (obj instanceof Reference) {
	// store as ref and add outAttrs
} else if (obj instanceof Serializable) {
	// serialize and add outAttrs
} else if (obj instanceof DirContext) {
	// grab attributes and merge with outAttrs
} else {
	... 
}

これらの例で示したように、コンテキスト実装はさまざまなタイプのオブジェクト (ReferenceSerializableDirContext) を保存できる場合があります。コンテキスト実装が直接 Referenceable オブジェクトを保存できないが getStateToBind() がそのようなオブジェクトを返すときは、コンテキスト実装は代わりに Referenceable.getReference() を呼び出して結果の Reference を保存するものとします。

さまざまなタイプのオブジェクトを保存できるコンテキスト実装の場合、次の一般的なタイプについては、この順序に従うものとします。

この順序を推奨する理由は、bind()/rebind() メソッドの呼び出し側の意図をもっとも的確に示す順序と考えられるからです。たとえば、ReferenceSerializable なので、Serializable チェックを最初に実行すると、どの Reference オブジェクトも参照形式では保存されません (すべて直列化される)。

2.5 連合のサポート

2.5.1 名前

コンテキストに文字列の名前の引数が与えられたとき、その名前は複数の名前空間にまたがる可能性のある合成名を表すか、または、単一の名前空間に属する単一の複合名要素 (単一または複数の基本名から作られている) が含まれます。コンテキスト実装は名前のどの部分をコンテキスト内で解釈または処理するかを判断し、残りを次のコンテキストに渡さなければなりません。これは、構文解析的に名前を調べて行なっても、動的に名前を解釈して行なってもかまいません。

コンテキストは CompositeName のインスタンスの場合には、Name 引数を与えられ、この引数は合成名として扱われます。それ以外の場合は、CompoundName クラスまたはその他の複合名実装によって実装される複合名として扱われます。

2.5.2 コンテキストを通じた解釈処理

コンテキストは、すべてのコンテキスト操作の解釈フェーズを実行することにより連合に参加します。lookup() メソッドは必ずサポートしなければなりません。その他のメソッドのサポートは任意ですが、連合に参加するコンテキストの場合は、すべての操作で暗黙の解釈処理をサポートしなければなりません。

図 1: bind() の実行のための、中間コンテキストを通じた解釈の例

bind() の実行のための、中間コンテキストを通じた解釈の例

たとえば、bind() 操作をサポートしないコンテキストを考えます。このコンテキストが bind() のための中間コンテキストとして使用されると、操作を次のコンテキストに続けるために、このコンテキストは操作の解釈処理部分を実行しなければなりません。このコンテキストが内部でのバインディングの作成のみを要求されている場合には、単に OperationNotSupportedException をスローするだけです。図 1 は、bind() 操作を目的のコンテキストで実行するために中間コンテキストを通じて渡す方法を示します。

2.5.3 コンテキストのサブインタフェースを通じた解釈処理

DirContext メソッド (getAttributes() など) を呼び出すために、アプリケーションは最初に初期の DirContext を取得して、次に DirContext 上で操作を実行します。

DirContext ctx = new InitialDirContext();
Attributes attrs = ctx.getAttributes(someName);

コンテキスト実装の視点から見ると、属性を取得するために getAttributes() が複数のネーミングシステムをたどることが必要な場合があります。これらのネーミングシステムのいくつかは Context インタフェースのみをサポートし、DirContext インタフェースをサポートしていません。これらのネーミングシステムは、ターゲットのコンテキストに向けての解釈のための中間的存在として使用されます。ターゲットのコンテキストは DirContext インタフェースをサポートしなければなりません。この例を図 2 に示します。

図 2: DirContext でない中間コンテキストを通じた解釈処理

DirContext でない中間コンテキストを通じた解釈処理

中間のネーミングシステムが Context の拡張への連合に参加するためには、それらのシステムは Resolver インタフェースを実装しなければなりません。Resolver インタフェースは、Context の特定のサブインタフェースをサポートしない中間コンテキストを通じて解釈を行うために JNDI フレームワークによって使用されます。このインタフェースは、resolveToClass() メソッドの 2 つのオーバーロード形式により構成されます。このメソッドは名前を部分的に解釈するために使用され、要求されたサブインタフェースのインスタンスである最初のコンテキストで終わります。このメソッドと Context インタフェース内のすべてのメソッドの解釈フェーズのサポートを提供することにより、コンテキスト実装は、Context の拡張機能 (サブインタフェース) のための中間コンテキストとして機能します。

public interface Resolver {
	public ResolveResult resolveToClass(Name name, Class contextType)
throws NamingException; public ResolveResult resolveToClass(String name, Class contextType)
throws NamingException; }

2.5.4 ネーミングシステムの境界

(複数要素の) 合成名の解釈は 1 つのネーミングシステムから次のネーミングシステムに進み、複数のネーミングシステムにまたがった要素の解釈は一般に、各々のネーミングシステムに対応するコンテキスト実装により処理されます。コンテキスト実装の視点から見ると、コンテキスト実装は自分の責任範囲外の要素を次のネーミングシステム (のコンテキスト実装) に渡します。

次のネーミングシステムのためのコンテキスト実装を探す手段はいくつかあります。「明示的」な方法として、1 つのネーミングシステムが次のネーミングシステム内のコンテキスト (またはコンテキストへの Reference ) に結びつく「接続点」を使用して探す方法があります。たとえば、合成名「cn=fs,ou=eng/lib/xyz.zip」で、LDAP 名「cn=fs,ou=eng」が解釈されて、1 つのファイルシステムコンテキストに渡され、次にその中で「lib/xyz.zip」が解釈されます。

別の方法として、次のネーミングシステムを「暗示的」に探すこともできます。たとえば、コンテキスト実装は自分が解釈したオブジェクトのサービスに特定の知識に基づいて、次のネーミングシステムを選ぶことができます。たとえば、合成名「ldap.wiz.com/cn=fs,ou=eng」で、DNS 名 ldap.wiz.com により DNS エントリの名前が定まります。DNS の先の、次のネーミングシステムを探すために、DNS コンテキスト実装は、エントリ内で見つかった SRV リソース記録 (この場合は LDAP コンテキストを指名する) を使ってコンテキストを構築できます。 この場合には、偶然 LDAP コンテキストの名前が定まります。次のネーミングシステムをこの方法で探した場合は、1 つのネーミングシステムから次のネーミングシステムへの境界を示すために JNDI の合成名区切り文字が使用され、この区切り文字は「次のネーミングシステムへの暗示的ポインタ」と見なされます。

次のネーミングシステムが見つかっても、コンテキスト実装は次のネーミングシステムに合成名の残りの未解釈部分を渡さなければなりません。

2.5.5 連合内での操作の続行

複数の名前空間にまたがる名前に対する操作を実行するとき、中間のネーミングシステムのコンテキストは次のネーミングシステムに操作を渡す必要があります。コンテキストはこのために、操作がどこまで進んだかを示す情報を含む CannotProceedException を最初に作成します。この処理の中で、コンテキストは例外の解釈済みオブジェクト (resolved object)、解釈済み名称 (resolved name)、未解釈名称 (remaining name)、環境 (environment) の部分を設定します2 (Context.rename() メソッドの場合は解釈済み新名称 (resolved newname) の部分も設定)。

次にコンテキストは CannotProceedException を static メソッド NamingManager.getContinuationContext()に渡すことにより、JNDI から「継続コンテキスト」を取得します。

public class NamingManager {
	public static Context getContinuationContext(
		CannotProceedException e) throws NamingException;
	...
}

getContinuationContext() は例外の中の情報を使用して、操作を続行するためのコンテキストインスタンスを生成します。

DirContext 操作の継続コンテキストを取得するために、Directory-Manager.getContinuationDirContext() を使用します。

public class DirectoryManager {
	public static getContinuationDirContext(
		CannotProceedException e) throws NamingException;
	...
}
/blockquote>

継続コンテキストを取得したら、名前の未解釈の残り部分を使って操作を続行します。

たとえば、bind() 操作を続行しようとする場合は、コンテキスト実装の中のコードは次のようになります。

public void bind(Name name, Object obj) throws NamingException {
	... 
	try {
		internal_bind(name, obj);
		...
	} catch (CannotProceedException e) {
		Context cctx = 			NamingManager.getContinuationContext(e);
		cctx.bind(e.getRemainingName(), obj);
	}	
}

この例で bind() は、バインドの実際の処理の実行と、処理がこのネーミングシステムを越えることがわかったときの CannotProceedException 例外のスローを、内部メソッド internal_bind() に依存します。次に、操作を続行するために、例外は getContinuationContext() に渡されます。操作を続行できないときは、継続コンテキストは CannotProceedException を元の bind() 操作の呼び出し元に対してスローします。

2.5.6 次のネーミングシステムの「動的」な検出

連合の構成によっては、1 つのネーミングシステムの解釈結果が次のネーミングシステムを示さないことがあります。コンテキスト実装に得られる結論は、「解釈は現在のネーミングシステムで終わったが、次のネーミングシステムに進まなければならない」ことです。

たとえば、合成名「lib/xyz.zip/part1/abc」が、ZIP 形式のファイルの名前を指定する「lib/xyz.zip」と、ZIP ファイル内のエントリの名前を指定する「part1/abc」の 2 つの部分で構成されているとします。「lib/xyz.zip」の解釈結果はファイルオブジェクトですが、必要な結果は ZIP エントリの名前を解釈するコンテキストです。同様に、ある合成名が tar 形式のファイル内のエントリの名前を指定している場合は、その合成名のファイル要素に必要な解釈結果は tar エントリを解釈するコンテキストです。

実際には、ファイルの形式によっては、ファイルシステムの名前空間下ではどんなタイプのコンテキストでも連合がなされる可能性があります。このような関係は対称であるべきです。つまり、ZIP ファイルコンテキストとそれに類似した他のコンテキストは、その他の、ファイルシステム以外の名前空間の下で連合が可能でなければなりません。さらに、ファイルシステムのコンテキスト実装の開発者および、ZIP ファイル、tar ファイル、その他の未定義の形式のための各コンテキスト実装の開発者が、独立して作業できるようにするべきです。

このようなタイプの連合をサポートするために、JNDI には「nns 参照」(nns:next naming system) と呼ばれる特別な形の Reference が定義されています。この Reference には、nns というタイプアドレスがあります。このアドレスコンテンツは解釈済みのオブジェクト (上の例では ZIP ファイル) です。ファイルシステムの例では、ファイルシステムのコンテキスト実装は次のような nns 参照を作成します。

 RefAddr addr = new RefAddr("nns") {
	public Object getContent() {
 		return theFile;
	}
};
Reference ref = new Reference("java.io.File", addr);

次に、コンテキスト実装は、nns 参照を解釈済みオブジェクトとして使用することにより、CannotProceedException を作成し (「接続点」の場合と同様に)、解釈済みファイル名と空白要素から成る解釈済みの名前を作成します。空白要素は次のネーミングシステムへの暗示的なポインタとして使用され、次のネーミングシステムへの位置まで解釈が進んだことを示します。解釈済みのオブジェクトと解釈済みの名前の値がどのように対応するかに留意してください。コンテキスト実装は次に、CannotProceedExceptiongetContinuationContext() に渡します。

CannotProceedException 内の解釈済みオブジェクト (resolved object) の場合のように、getContinuationContext() はこの nns 参照を受け取るコンテキスト実装を探します。たとえば ZIP ファイルのコンテキスト実装は、nns 参照と、ファイルの名前 (与えられたコンテキストに対する相対名) などの情報を受け取ります。コンテキスト実装は、そのファイルが ZIP ファイルであると判断すると、そのファイル内の名前を解釈するためのコンテキストを生成します。

2.5.7 CannotProceedException の詳細

連合のための JNDI SPI のフレームワークの中心は、CannotProceedException です。CannotProceedException には、NamingException スーパークラスから継承した、解釈済みの名前または解釈済みのオブジェクトや未解釈の名前などの情報が含まれています。さらに、CannotProceedException には「代替」名と「代替」名コンテキストも含まれます。NamingException から解釈された名前が完全な合成名 (操作の最初のコンテキストに相対の名前) であるのに対し、代替名は代替名コンテキストに相対の解釈済み名です。つまり、代替名は解釈済みの名前と同じでなくてもかまいません。代替名と代替名コンテキストは NamingManager/DirectoryManager.getObjectInstance() への引数として使用されます。これらの引数により、このメソッドが呼び出したファクトリが解釈済みオブジェクトに関する詳細な情報を取得することができます (たとえば、オブジェクトに関する特別な属性を取得するために使用するなど)。これらのファクトリについては、第 4 章に説明されています。

2.5.8 コンテキスト上の情報

JNDI SPI フレームワークでは「前方検索」が重視され、次のネーミングシステムの検出が試みられます。 しかし、コンテキスト実装によっては、検出されたあとに解釈のチェーンを「後ろ向きに」たどってコンテキスト上の情報を得ることが必要です。たとえば、ホストネーミングシステムの連合から外された特定のコンテキスト実装が、「そのコンテキスト実装がホスト情報を見つける唯一の方法は、自分の上位 (直接の上位ではない可能性がある) のネーミングシステムに問い合わせることである」ような設計になっているかもしれません。それを行うには、そのコンテキスト実装には、解釈が現在点までどのように進んできたのかに関する「コンテキスト上の情報」が必要です。

連合に関してすでに説明したことをまとめると次のようになります。 複数の名前空間にまたがる名前に操作を実行するときは、コンテキスト実装は最初に、解釈がどこまで進んでいるかを示す情報を含む CannotProceedException を生成します。コンテキスト実装は次に、getContinuationContext()を呼び出すことにより JNDI から継続コンテキストを取得します。コンテキスト上の情報の取得をサポートするために、getContinuationContext() は継続コンテキストの環境に java.naming.spi.CannotProceedException 環境プロパティを Cannot-ProceedException 引数の値を指定して自動的に追加します。このプロパティは継続コンテキストによって継承され、そのコンテキストの実装が例外のフィールドを調べるために使用されます。

2.6 照会のサポート

LDAP スタイルのディレクトリサービスでは、クライアントの要求を他のサーバにリダイレクトするための「照会 (referral)」の概念がサポートされています。照会は、すでに説明した連合継続の機構とは異なります。 照会は JNDI クライアントに示され、次にそれを追跡するかどうかはクライアントが決定できます。 これに対し、CannotProceedException が返されるのは、これ以上解釈を進めることができない場合だけです。もう 1 つの違いは、個々のコンテキスト実装が、照会を使って操作を継続する (およびそれを行う機構を自身で決定する) 能力を提供する点です。連合では、継続の機構は個々のコンテキスト実装の範囲外で、個々のコンテキスト実装は、JNDI SPI フレームワークが提供する共通の連合機構を利用します。

照会をサポートするコンテキスト実装は、ReferralException のサブクラスを定義し、その抽象メソッドに実装を提供します。getReferralContext() は操作を続行する位置でコンテキストを返し、getReferralInfo() は照会先の情報を、そのコンテキスト実装に適した形式で返します。

環境プロパティ java.naming.referral は、コンテキスト実装が照会を処理する方法を指定します。照会に遭遇したとき、または照会の追跡中に問題に遭遇したときに例外をスローするよう求められているコンテキスト実装では、アプリケーションに対して ReferralException をスローします。操作を続行するために、アプリケーションは元のメソッドに与えたのと同じ引数を使って、照会コンテキストについてメソッドを再度呼び出します。次のコード例は、アプリケーションが ReferralException を使用する方法を示します。3

while (true) {
	try {
		bindings = ctx.listBindings(name);
		while (bindings.hasMore()) {
			b = (Binding) bindings.next();
			...
		}
		break;
	} catch (ReferralException e) {
		ctx = e.getReferralContext();
	}
}

アプリケーションが、元の引数を使ってメソッドを再び呼び出すというこの慣習に従うのは簡単です。この方法では、操作を続行するための十分な情報を照会コンテキストの実装に与えるために、ReferralException の実装に負担がかかります。再度呼び出される操作に引数が余分に渡される場合があります。照会コンテキストの実装は、余分な情報や不必要な情報を無視してもかまいません。

操作から、照会に追加して別の結果が返される場合があります。たとえば、コンテキストを探しているとき、いくつかの照会に加えて、どこで詳細結果が得られるかに関する複数の結果をサーバが返すことがあります。これらの結果と照会は、プロトコルレベルでインタリーブされる場合があります。照会がユーザの介入を必要とする (つまり、自動的には追跡されない) 場合は、最初に検索の列挙を通じて結果を返すものとします。結果が返されると、照会例外をスローできます。これにより、シンプルなプログラミングモデルを使って、照会とその結果セットとの間の明確な関係をユーザに示すことができます。

2.7 スキーマのサポート

JNDI には、ディレクトリ内の属性を表現するために Attribute インタフェースが定義されています。属性は、1 つの属性識別子 (文字列) と 1 セットの属性値によって構成されます。 属性値は、Java プログラミング言語の任意のオブジェクトです。Attribute には、属性の定義と構文定義をディレクトリのスキーマから取得するためのメソッドも定義されています。

public class Attribute {
	public DirContext getAttributeDefinition() throws NamingException;
	public DirContext getAttributeSyntaxDefinition()
throws NamingException; ... }

ユーティリティクラス BasicAttribute には、これらのメソッドの有用な実装はありません。このようなスキーマ情報をサポートするディレクトリコンテキスト実装は、そのスキーマ機構に基づくこれらの 2 つのメソッドを実装する (おそらく BasicAttribute のサブクラスを作成し、これらの 2 つのメソッドをオーバーライドすることにより)、Attribute の実装を提供するものとします。次に、コンテキスト実装は Attribute のインスタンスを返すよう要求されると、これらのサブクラスのインスタンスを返すものとします。コンテキスト実装は、これらの 2 つのメソッドの有意な実装を持たない Attribute インスタンスを受け取った場合は、その属性の定義と構文を判断するために適切なデフォルトを使用し、属性値のクラス名や属性の識別子に使われている規約などの情報を使うものとします。

DirContext インタフェースには、次のスキーマ関連メソッドがあります。

public class DirContext {
	...
	public DirContext getSchema(Name name) throws NamingException;
	public DirContext getSchema(String name) throws NamingException;

	public DirContext getSchemaClassDefinition(Name name) 
		throws NamingException;
	public DirContext getSchemaClassDefinition(String name) 
		throws NamingException;

}

getSchema() は指名されたオブジェクトへのスキーマツリーを返し、getSchemaClassDefinition() は指名されたオブジェクトのスキーマクラス定義を含むコンテキストを返します。単一のグローバルなスキーマを持つシステムでは、name 引数の値にかかわらず、同じスキーマツリーを返します。きめ細かなスキーマ定義をサポートするシステムでは、調査されるコンテキストによって異なるスキーマツリーを返します。

2.8 イベントのサポート

コンテキスト実装は、EventContext/EventDirContext インタフェース内のメソッドの実装を提供することにより、イベント通知をサポートします。これらのインタフェースにより提唱されるイベントモデルは、マルチスレッドモデルを使って簡単にサポートできます。アプリケーションが addNamingListener() を使ってコンテキストにリスナーを登録すると、コンテキストは要求を記録し、イベントを生成するために必要な情報を集めるための処理を開始します。コンテキストはイベントを生成するための情報を受け取ると、そのイベントをリスナーにただちに渡します。普通、登録を行うスレッドはリスナーを実行するスレッドとは異なります。また普通、コンテキスト実装は自分が作成したスレッドを使用し、リスナーメソッドの実行を管理します。1 つのイベントが複数のリスナーにディスパッチされた場合は、コンテキスト実装はリスナーメソッドを同時に別々のスレッドで実行することを選択できます (一般的にはこれを推奨)。

addNamingListener() メソッドは NamingListener のインスタンスを受け取ります。このインスタンスには、NamingListener のサブインタフェースが 1 つ以上実装されていることがあります。リスナーが複数のサブインタフェースを実装している場合は、コンテキスト実装は、登録を満たすために必要なリソースの保存を試みるものとします。たとえば、1 つの実装が複数のサブインタフェースの要求をすべて捕らえる単一の要求をサーバに発行します。

コンテキストがさらにイベントを発生させることができない場合には、可能であれば、コンテキスト実装はリスナーに対して NamingExceptionEvent を発生させ、自動的にそのリスナーの登録を取り消すことができるものとします。たとえば、サーバへの接続がリスナーの登録の後で切断され、イベントを渡すための情報が利用できない場合には、コンテキストは NamingExceptionEvent をリスナーにただちに渡します。

2.9 コンテキスト環境のサポート

Context (またはそのサブインタフェース) の各インスタンスには、そのコンテキストが提供するサービスへのアクセス方法の、アプリケーションにより表現されるプリファレンスを含む「環境」を関連付けることができます。環境の中にある情報の例には、ユーザの資格と望まれるセキュリティレベル (nonesimplestrong)、構成情報 (使用するサーバなど) を指定するセキュリティ関連情報があります。環境プロパティの詳細は、JNDI API ドキュメントの第 6 章と付録 A を参照してください。

一般に環境プロパティは最大限の可搬性を保証するために定義されます。個々のサービスプロバイダはこれらの汎用プロパティを、そのサービスに適応した特性にマップします。プロバイダに関係のないプロパティは記録するにとどめて、無視します。サービスプロバイダに固有のプロパティまたはプリファレンスの、異なるプロバイダにまたがる適用性が制限されている場合には、それらのプロパティまたはプリファレンスを保存するためにも環境を使用できます。

2.9.1 プロパティの命名規約

JNDI API ドキュメントの節 6.1 に、環境プロパティがどのように命名されるかの説明があります。サービスプロバイダに固有のプロパティには、プロバイダに対する一意性を反映する接頭辞を付けます。サービスプロバイダのパッケージ名を先頭に付ける手法がよく使われます。たとえば、Sun の LDAP プロバイダは主に com.sun.jndi.ldap パッケージに含まれるので、Sun の LDAP プロバイダに固有のプロパティには、先頭に「com.sun.jndi.ldap.」が付きます。

2.9.2 コンテキストの環境の初期化

初期コンテキストを作成するとき (InitialContext またはそのサブクラスからのコンストラクタを使用)、アプリケーションは環境をパラメータとして提供できます。パラメータは、Hashtable またはそのサブクラスのいずれか (Properties など) として表現されます。JNDI クラスライブラリは、このパラメータと他のソースからデータを拡張し (JNDI API ドキュメントの第 6 章を参照)、これをコンテキスト実装に渡します。

他のすべてのパラメータと同様に、コンテキスト実装が受け取った環境パラメータは呼び出し側が所有しています。コンテキスト実装は、受け取った環境パラメータのコピーを作成するか、「呼び出し側がパラメータに加えた変更がコンテキスト実装から見えるものに影響しないこと、およびその逆を保証する」手段をとるものとします。また、環境パラメータが Properties のインスタンスである場合は、パラメータ上での列挙と Hashtable.get() では最上位のプロパティのみが調べられます (入れ子にされたデフォルトは調べない)。これは、予期される動作です。コンテキスト実装には、Properties インスタンスの入れ子にされたデフォルト内の値の取得または列挙は予期されません。

JNDI ライブラリは、異なるソース (初期コンテキストへの環境パラメータ、リソースファイル、適用される場合、システムプロパティとアプレットパラメータなど) からプロパティをマージする責任を負います (JNDI API ドキュメントの第 6 章を参照)。コンテキスト実装は普通、与えられた環境から必要なプロパティだけを読み込みます。コンテキスト実装が他のソースを参考にする必要はほとんどありません。

2.9.3 継承

環境は、コンテキストメソッドがコンテキストから次のコンテキストに進むときに、親から子に継承されます。特定のコンテキストによって無視されるプロパティが環境内にあるかどうかにかかわらず、コンテキストインスタンスの環境全体が、子コンテキストのインスタンスに継承されます。

この環境「継承」特性を実装するには、コンテキスト実装はコンテキストインスタンスから次のコンテキストインスタンスに環境を引き渡さなければなりません。1 つのコンテキスト実装の中では、Context コンストラクタへの引数、または Context インスタンスを生成するための NamingManager/DirectoryManager.getObjectInstance() メソッドへの引数として環境を渡すことにより、これを行うことができます。

連合内の複数のコンテキスト実装にまたがる場合は、NamingManager.getContinuationContext()/DirectoryManager.getContinuationDirContext()CannotProceedException パラメータの一部として環境を渡すことにより、これがサポートされます。 渡された環境は次に、操作を続行するコンテキストのインスタンスを生成するときに使用されます。

継承は、「各々のコンテキストが独自の環境の視野を持つ」というセマンティクスが維持されている限りは、どんな方法で実装してもかまいません。たとえば、絶対必要になるまで環境のコピーを延期するために、copy-on-write 実装を使うことができます。

2.9.4 環境の更新

コンテキストの環境は、Context インタフェースの addToEnvironment() メソッドと removeFromEnvironment() メソッドの使用を通じて更新できます。

public interface Context {
	...
	public Object addToEnvironment(String propName, Object propVal) 
		throws NamingException;

	public Object removeFromEnvironment(String propName) 
		throws NamingException;
}

これらのメソッドは、Context の「この」インスタンスの環境を更新します。コンテキスト実装に関係しない環境プロパティは無視されますが、環境の一部として維持されます。更新された環境は Context のこのインスタンスに影響し、すべての新しい子 Context インスタンスに継承されますが、すでに存在する Context インスタンスには影響しません。Context 上で空白名をルックアップすると、他の子に継承されたものと同じ環境を持つ新しい Context インスタンスが返されます。

詳細は、JNDI API ドキュメントの節 6.6 を参照してください。

2.9.5 プロバイダリソースファイル

サービスプロバイダには各々、そのプロバイダに固有のプロパティを含むオプションのリソースファイルがあります。このリソースの名前は次のようになります。

[prefix/]jndiprovider.properties 

ここで prefix は、プロバイダのコンテキスト実装のパッケージ名で、ピリオド (「.」) はスラッシュ(「/」) に変換されます。たとえば、あるサービスプロバイダがクラス名 com.sun.jndi.ldap.LdapCtx のコンテキスト実装を定義するとします。このプロバイダのためのプロバイダリソースは、com/sun/jndi/ldap/jndiprovider.properties と指定されます。

JNDI クラスライブラリは、JNDI API ドキュメントの節 6.5.2 に説明されているように、プロパティの値を決める必要があるときは、このファイルを調べます。

プロパティの値を決める必要があるとき、サービスプロバイダは一般に、環境から直接値を取得します。自分のプロバイダリソースファイルに格納する固有のプロパティを、サービスプロバイダが定めることもあります。その場合は、それらのプロパティを自分のプロパティリソースファイルから読み込んでから、JNDI API ドキュメントの節 6.5.2 で説明するアルゴリズムに従った方法でマージしなければなりません。

2.10 接続の管理

クライアント/サーバプロトコルを使用するコンテキスト実装では、クライアントとサーバの間の接続とコンテキストの間に 1 対 1 のマッピングは必要ではありません。JNDI は、接続を直接扱わない高レベル API です。必要な接続管理はコンテキスト実装によって行われます。そのため、複数のインスタンスで 1 つの接続を共有することもあり、またコンテキスト実装は独自のアルゴリズムを使って接続とネットワークの使い方を保存してもかまいません。したがって、コンテキストインスタンスでメソッドが呼び出されるときに、コンテキスト実装は要求された操作を実行する以外に何らかの接続管理を行う必要がある可能性があります。

Context.close() メソッドと NamingEnumeration.close() メソッドを使ってアプリケーションは、接続関連のリソースをいつ解放するかに関するヒントをコンテキスト実装に与えることができます。コンテキスト実装は、ガベージコレクションと接続関連リソースの保存のための他の手段を使うことを選択できます (一般的にはこれを推奨)。

いくつかの環境プロパティは、コンテキストの接続に影響します。たとえば、アプリケーションがセキュリティ関連のプロパティを変更すると、コンテキスト実装がそれらの更新されたプロパティを使って接続を修正したり新しく作成することが必要になる場合があります。変更の前に、接続が他のコンテキストと共有されていた場合は、プロパティが更新されていないコンテキストに接続の変更が影響してはなりません。

3 初期コンテキスト

すべてのネーミングメソッドはコンテキストに関係して実行されるので、アプリケーションにはこれらのメソッドを起動するために最初のコンテキストが必要です。この最初のコンテキストを、「初期コンテキスト」と呼びます。初期コンテキスト内のバインディングはポリシーセットにより決定され、その後は初期コンテキスト実装により決定されます。 その決定には、おそらくグローバルな企業全体規模の名前空間のネーミングのための標準ポリシーが使用されます。初期コンテキストには、たとえば、インターネット DNS 名前空間へのバインディング、企業全体規模の名前空間へのバインディング、アプリケーションを実行しているユーザに属する個人ディレクトリへのバインディングが 1 つずつ含まれている場合などがあります。

アプリケーションは、次の呼び出しを行うことにより初期コンテキストを取得します。

Context ctx = new InitialContext();

代替コンストラクタにより環境を引数として渡すことができます。これにより、アプリケーションは初期コンテキストの作成で使用されるプリファレンスまたはセキュリティ情報を引き渡すことができます。

Hashtable env = new Hashtable();4
env.put(Context.SECURITY_PRINCIPAL, "jsmith");
env.put(Context.SECURITY_CREDENTIALS, "xxxxxxx");
Context ctx = new InitialContext(env);

アプリケーションは、初期コンテキストの取得に続いて、Context メソッドの呼び出しを実行できます。

Object obj = ctx.lookup("this/is/a/test");

InitialContext クラス (およびサブクラス) は、デフォルトのアルゴリズムを使って実装を選択します。 このアルゴリズムは、「初期コンテキストファクトリビルダ」(この次に説明) をインストールすることによりオーバーライドできます。

InitialDirContextInitialContext のサブクラスです。初期コンテキストを使ってディレクトリ操作を行うために使用されます。 InitialLdapContext クラスは InitialDirContext のサブクラスです。初期コンテキストを使って特別な LDAP v3 操作を行うために使用されます。ここで説明したアルゴリズムとポリシーは、InitialDirContextInitialLdapContext にも当てはまります。説明の Context を適宜 DirContext または LdapContext に置き換えてください。

3.1 初期コンテキストファクトリ

「初期コンテキストファクトリ」は、第 2 章で概説したガイドラインに従って実装されたコンテキストのインスタンスを生成するクラスです。このファクトリは、InitialContext クラス (またはサブクラス) のコンストラクタによって使用されます。

環境を与えられると、ファクトリは Context (またはそのサブインタフェース) のインスタンスを返します。

public interface InitialContextFactory {
	public Context getInitialContext(Hashtable env)         
throws NamingException; }

付録 A に、InitialContextFactory の例があります。

コンテキストインスタンスが生成されたあとは、InitialContext 上で URL 以外の名前 (下記参照) を使ってメソッドが呼び出されると、そのメソッドはコンテキストインスタンスに渡され、そこで呼び出されます。

JNDI は、使用する初期コンテキスト実装を java.naming.factory.initial プロパティを使って選択します。このプロパティには、1 つの初期コンテキストファクトリの完全修飾クラス名が含まれています。このクラスは InitialContextFactory インタフェースを実装し、引数をとらない public のコンストラクタを持たなければなりません。JNDI はこの初期コンテキストファクトリクラスをロードしてから、その上で getInitialContext() を呼び出して、初期コンテキストとして使用される Context インスタンスを取得します。

特別な初期コンテキストを使おうとするアプリケーションは、InitialContext (またはサブクラス) コンストラクタに渡された環境の中に、またはリソースファイル、システムプロパティ、アプレットパラメータのいずれかを通じて、java.naming.factory.initial プロパティを提供しなければなりません。

3.1.1 例外

java.naming.factory.initial プロパティが null 以外の値に設定されているときは、InitialContext (およびサブクラス) コンストラクタは初期コンテキストファクトリをロードしてそのインスタンスを生成しようとします。 次に、初期コンテキストファクトリがコンテキストインスタンスを生成します。認証の問題などの原因で、ファクトリまたはコンテキストを作成できないときは、初期コンテキストファクトリは例外をスローして、この問題を通知することができます。ただし、環境プロパティまたは接続に関する問題を確認して初期コンテキストのユーザに通知する時期は、コンテキスト実装に任されています。問題の確認と通知は、コンテキストで操作が実行されるまで遅らせても、コンテキストが作成されたときにすぐに行なってもかまいません。

java.naming.factory.initial プロパティが設定されていない場合は、初期コンテキストに代わって背後のコンテキストを作成する処理は試行されません。初期コンテキストは、たとえば次で説明するように、URL 名の処理に使用し続けることができます。

3.2 URL のサポート

URL5 文字列が初期コンテキストに渡された場合は、対応する「URL コンテキスト実装」を使って解釈されます。この機能は InitialContext クラス (およびサブクラス) によってサポートされ、java.naming.factory.initial 環境プロパティの設定とは無関係です。

この機能により、ある URL コンテキスト実装が利用可能な任意の名前空間に、アプリケーションが初期コンテキストを使って到達することが可能になります。たとえば、次のコードは初期コンテキストから、ある LDAP 名前空間のリストを作成します。

new InitialContext().list("ldap://lserver/ou=eng,o=wiz,c=us");

3.2.1 URL コンテキスト

URL 文字列の書式は次のとおりです。

scheme_id:opaque_string

たとえば、LDAP URL 文字列がスキーマ ID「ldap」を持ち、ファイル URL がスキーマ ID「file」を持つとします。

URL コンテキスト実装は Context インタフェース (とサブインタフェース) を実装するクラスで、サポートするスキーマの URL 文字列である名前の引数を受け取ります。たとえば、LDAP URL コンテキストは URL 文字列「ldap」を受け取ります。

URL 文字列名が URL コンテキストに渡されると、String を受け取るコンテキストメソッドは、その名前を URL スキーマにより定義される構文を持つ URL 名として取り扱います。最初の要素が URL 文字列名である Name オブジェクトが URL コンテキストに渡されると、最初の要素が URL 文字列として取り扱われ、残りは連合に使用されます (つまり、最初の要素の解釈により、残りの解釈に使用するネーミングシステムが示される)。Name インスタンスは CompositeName であるべきで、それ以外の場合は InvalidNameException をスローするものとします。

URL 文字列以外の名前引数と、不適切なスキーマ ID を持つ URL 文字列は InvalidNameException をスローして拒否するものとします。

3.2.2 URL コンテキストファクトリ

「URL コンテキストファクトリ」は、1 つ以上のスキーマのための URL コンテキストのインスタンスを生成するクラス (実際には特別なタイプの「オブジェクトファクトリ」(節 4.1 を参照)) です。

InitialContext クラスは名前引数として URL 文字列を受け取ると、次のアルゴリズムを使って URL コンテキストファクトリを探します。環境プロパティ java.naming.factory.url.pkgs には、コロンで区切られたパッケージ接頭辞のリストがあります。ファクトリのクラス名は、プロパティのリストに示される各パッケージ接頭辞について次の規則を使って構成されます。

package_prefix + "."+ scheme_id + "."+ scheme_idURLContextFactory

デフォルトのパッケージ接頭辞 com.sun.jndi.url がリストの最後に付いています。

たとえば、URL が「ldap://somehost:389」で、java.naming.factory.url.pkgs に「com.widget:com.wiz.jndi」が含まれている場合、InitialContext クラスは、次のクラスをロードしていずれかのインスタンスの生成に成功するまで試すことにより、対応するファクトリクラスを探します。

com.widget.ldap.ldapURLContextFactory
com.wiz.jndi.ldap.ldapURLContextFactory
com.sun.jndi.url.ldap.ldapURLContextFactory

ファクトリクラスは ObjectFactory インタフェース (「URL コンテキストファクトリ」を参照) を実装し、引数をとらない public のコンストラクタを持ちます。InitialContext クラスは、ファクトリの getObjectInstance() メソッドに解釈済みオブジェクトとしてスキーマ ID を渡し、このメソッドはその URL スキーマのための URL コンテキストを作成します。URL コンテキストは次に、最初に InitialContext に与えられた URL で目的の Context または DirContext 操作を行うために使用されます。

3.2.3 サービスプロバイダの責任

サービスプロバイダが URL コンテキストファクトリと URL コンテキスト実装を提供しなければならないという条件はありません。サービスプロバイダがこれらを提供するのは、URL スキーマを持つ URL 文字列名を InitialContext クラスに受け取らせたい場合だけです。たとえば、サービスプロバイダが、初期コンテキストファクトリと、そのファクトリを通じてアクセスされるコンテキスト実装だけを提供してもかまいません。

3.3 デフォルト動作のオーバーライド

java.naming.factory.initial 環境プロパティを使った初期コンテキストファクトリの作成と URL サポートのポリシーは、InitialContext クラスに組み込まれています。このポリシーのすべてまたは一部をアプリケーションがオーバーライドする方法は 2 つあります。

3.3.1 URL サポートの除外

アプリケーションで URL 文字列を扱いたくない場合は、NamingManager.getInitialContext() メソッドを使用できます。 このメソッドは、java.naming.factory.initial 環境プロパティで指定されているファクトリを使ってコンテキストインスタンスを生成します。

このメソッドは、アプリケーションが初期コンテキストファクトリが作成したコンテキストによって実装されているインタフェースにアクセスする必要があるが、その初期コンテキストが ContextDirContextLdapContext のいずれでもない場合にも便利です。次のコードのフラグメントは、NamingManager.getInitialContext() を使ってコンテキストを取得し、それをサブクラスにキャストします。

FooContext ctx = (FooContext) NamingManager.getInitialContext(env);
...
Object obj = ctx.lookup(name);
ctx.fooMethod1(...);

初期コンテキストファクトリビルダ (後で説明) をインストールすると、NamingManager.getInitialContext() の結果に影響するので注意してください。

3.3.2 すべてのポリシーの除外

初期コンテキストファクトリ「ビルダ」は、初期コンテキストファクトリのインスタンスを生成するクラスです。

アプリケーションは、初期コンテキスト実装を探して構築する方法についての独自のポリシーを定義するために、初期コンテキストファクトリビルダをインストールすることができます。ビルダがインストールされている場合は、初期コンテキストファクトリの作成の責任をビルダが単独で負います。通常 JNDI が使用するデフォルトのポリシー (java.naming.factory.initial または URL サポート) は採用されません。

初期コンテキストファクトリビルダの実装は、InitialContextFactoryBuilder インタフェースを実装しなければなりません。このインタフェースの createInitialContextFactory() メソッドは InitialContextFactory のインスタンスを生成します。

ビルダがインストールされたあとは、アプリケーションは InitialContextInitialDirContextInitialLdapContext のうちいずれかのコンストラクタを使うか、NamingManager.getInitialContext() を使って初期コンテキストを取得できます。いずれかのコンストラクタを使う場合は、そのクラスの本質は、NamingManager.getInitialContext() から返される背後のコンテキスト実装のラッパーです。

3.4 InitialContext のサブクラスの実装

ContextDirContextLdapContext から拡張したインタフェースをサポートする初期コンテキストを準備する必要がある場合は、サービスプロバイダは InitialContextInitialDirContextInitialLdapContext のうちいずれかのサブクラスを提供する必要があります。

3.4.1 URL のサポート

InitialContextInitialDirContext が行うのと同じ方法での URL のサポートを追加するには、サブクラスは InitialContext で利用できる protected メソッドを次のように使用するものとします。これが、名前の引数を受け取るメソッドを持つインタフェースにとって意味のある唯一の方法です。

たとえば、FooContextDirContext のサブインタフェースであるとします。その初期コンテキスト実装は、使用する実際の初期コンテキストを取得する getURLOrDefaultInitFooCtx() メソッドを (NameString パラメータの両方に対し) 定義します。

public class InitialFooContext extends InitialDirContext {
	...
	protected FooContext getURLOrDefaultInitFooCtx(Name name)
		throws NamingException {
		Context answer = getURLOrDefaultInitCtx(name);
		if (!(answer instanceof FooContext)) {
			throw new NoInitialContextException("Not a FooContext");
		}
		return (FooContext)answer;
	}
	// similar code for getURLOrDefaultInitFooCtx(String name)
}

FooContext インタフェース内の、名前の引数を受け取る新しいメソッドの実装を行うときは、getURLOrDefaultInitFooCtx() を次のように使用します。

	public Object FooMethod1(Name name, ...) throws NamingException {
		return getURLOrDefaultInitFooCtx(name).FooMethod1(name, ...);
	}

3.4.2 新しいメソッドのサポート

FooContext インタフェース内の、名前の引数のないメソッドまたは URL サポートが不要なメソッドの実装を行うには、InitialContext.getDefaultInitCtx() を使用します。

protected FooContext getDefaultInitFooCtx() throws NamingException {
	Context answer = getDefaultInitCtx();
	if (!(answer instanceof FooContext)) {
		throw new NoInitialContextException("Not an FooContext");
	}
	return (FooContext)answer;
}
public Object FooMethod2(Args args) throws NamingException {
	return getDefaultInitFooCtx().FooMethod2(args);
}

3.4.3 コンストラクタ

実装には、クラスのための適切なコンストラクタを準備するものとします。コンストラクタは、スーパークラスの適切なコンストラクタを呼び出します。スーパークラスのコンストラクタを呼び出す前に環境を変更または検査する必要がある場合は、初期コンテキストの初期化を制御するための boolean 型のフラグを受け取る protected コンストラクタを使用し、次に、init() メソッドを使ってコンテキストを初期化します。次に例を示します。

public InitialFooContext(Hashtable environment, Object otherArg) 
	    throws NamingException {
	super(true); // don't initialize yet

	// Clone environment and adjust
	Hashtable env = (environment == null) ? new Hashtable(11) :
		(Hashtable)environment.clone();
	...
	init(env);
}

この新しい初期コンテキストを使うクライアントプログラムは、次のようになります。

import com.widget.jndi.InitialFooContext; ... FooContext ctx = new InitialFooContext(env); Object obj = ctx.lookup(name); ctx.FooMethod1(name, ...);

4 コンテキスト実装のカスタマイズ

JNDI では、コンテキスト実装がネームサービスまたはディレクトリサービスのオブジェクトを読み込んだり書き込んだりする方法を、アプリケーション、アプリケーションの開発者またはユーザ、あるいはサービスプロバイダがカスタマイズできます。LDAP v3 コントロールクラスのナロー変換についても、同様の機能を使用できます。

これらの機能は、コンテキスト実装にプラグインするモジュールとして考えることができます。

4.1 オブジェクトの読み込み: オブジェクトファクトリ

JNDI には名前空間に保存されている情報を使う、汎用的なオブジェクト (Context のインスタンスを含む) 生成方法があります。その情報のタイプ (java.lang.Object) は任意です。たとえば、Reference、URL、またはオブジェクトを生成するために必要な他のデータなどです。名前空間に保存されているこのような情報をオブジェクトに変換する処理は、「オブジェクトファクトリ」の使用を通じてサポートされます。オブジェクトファクトリは ObjectFactory インタフェース (または DirObjectFactory サブインタフェース) を実装するクラスです。

	public interface ObjectFactory {	
		public Object getObjectInstance(Object refObj, 
						Name name,
						Context nameCtx, 
						Hashtable env)
			throws Exception;
	}
	public interface DirObjectFactory extends ObjectFactory {
		public Object getObjectInstance(Object refObj, 
						Name name,
						Context nameCtx, 
						Hashtable env,
						Attributes attrs)
			throws Exception;
	}

あるオブジェクトに関する参照情報 (refObj)、そのオブジェクトの名前とバインドされている場所に関する追加情報、および追加の環境情報 (たとえば、オブジェクトを生成するユーザに関する ID 情報や認証情報など) を与えられると、ファクトリはその参照情報が表すオブジェクトを生成しようとします。たとえば、プリンタに関する参照情報を与えられると、プリンタオブジェクトファクトリは Printer のインスタンスを返します。 DirContext の実装とともに使用されるオブジェクトファクトリの場合には、ファクトリにはオブジェクトに関する属性も与えられます。ファクトリがさらに多くの属性または情報を必要とする場合は、name 引数または nameCtx 引数を使ってネームサービスまたはディレクトリサービスから直接情報を取得できます。

与えられた引数を使ってオブジェクトを生成できないときは、ファクトリは null を返すものとします。たとえば、プリンタオブジェクトファクトリにディスクドライブに関するデータが与えられた場合は null を返します。ファクトリは、他のオブジェクトファクトリを試みるべきではない場合にのみ例外をスローするものとします。そのため、ファクトリはその実装からスローされる実行時例外に留意すべきです。たとえば、プリンタオブジェクトファクトリにプリンタのデータが与えられたが、そのデータの形式が正しくない場合には、例外をスローします。

オブジェクトファクトリは JNDI の複数の場所で、基本的には参照情報をオブジェクトに変換するために使用されます。オブジェクトファクトリは、連合、初期コンテキストでの URL 処理、および (プリンタの例で説明したように) データをアプリケーションが期待する形式に変換するために使用されます。

4.1.1 構造化された参照の処理

Reference には、クラス名とオブジェクトファクトリの位置を返すためのメソッドが含まれます。次のメソッドが Reference にあります。

public class Reference {
	...
	public String 	getClassName();
	public String getFactoryClassName();
	public String getFactoryClassLocation();
}

ディレクトリサービスまたはネームサービスから読み出されたオブジェクトが Reference または Referenceable のインスタンスである場合は、対応するオブジェクトファクトリは、Reference 内の情報を使って探すことができます。getFactoryClassName() メソッドは ObjectFactory インタフェースを実装するファクトリクラスの名前を取得します。このファクトリは ObjectFactory インタフェースを実装し、引数をとらない public のコンストラクタを持たなければなりません。 getFactoryClassLocation() は、ファクトリのクラス実装のコードベースを取得します。 これは、空白文字で区切られた URL のリストです。

JNDI は ObjectFactory インスタンス上で、Reference と環境を引数として使用して getObjectInstance() を呼び出すことによりオブジェクトを生成します。結果は、getClassName() により識別されるクラスのインスタンスです。

アプリケーションに返されるオブジェクトのインスタンスを生成するために必要なすべてのクラスは、JNDI に準備されている機構を使って用意できます。アプリケーションは、ローカルでクラスをインストールする必要はありません。

図 3: 名前空間から参照を使ってオブジェクトを取り戻す例

名前空間から参照を使ってオブジェクトを取り戻す例

プリンタの例に戻り、あるプリンタを表すためのインタフェースが Printer で、そのインタフェースの実装が BSDPrinter クラスであるとします。BSDPrinterReferenceable インタフェースを実装し、BSDPrinter のインスタンスの構築方法に関する情報とプリントサーバとの通信のためのアドレス情報を Reference クラスを使って保存します。Reference には、オブジェクト (「Printer」) のクラス名、プリンタオブジェクトファクトリ (「PrinterFactory」) のクラス名、およびファクトリのクラス実装をロードするための URL が含まれます。ファクトリクラス名と実装位置を使って、JNDI は最初に PrinterFactory の実装をロードし、PrinterFactory のインスタンスを生成します。次に、JNDI はファクトリ上で getObjectInstance() を呼び出して、参照を使って Printer のインスタンスを生成します。たとえば、参照の中の 1 つのアドレスが「bsd」タイプのアドレスを持ち、プリントサーバのホスト名 (「lobby-printserver」) が含まれているとします。PrinterFactory インスタンスはアドレスタイプ (「bsd」) を使って BSDPrinter インスタンスの生成を決定し、アドレスコンテンツ (「lobby-printserver」) をコンストラクタに渡します。結果の BSDPrinter オブジェクトは、lookup() の結果として返されます。

コンテキスト実装の観点から見ると、これは NamingManager/DirectoryManager.getObjectInstance() を呼び出すことにより、すべて自動的に行われます。

アプリケーションが lookup() から返された BSDPrinter インスタンス上で print() を呼び出すと、データが印刷のために lobby-printserver マシン上のプリンタサーバに送られます。アプリケーションは、名前空間に保存されている Reference の詳細や、ジョブを実行するために使用するプロトコル、および BSDPrinter クラスがローカルで定義されているかネットワークを通じてロードされたかどうかについて通知される必要はありません。背後のサービスに保存されている情報を、Printer インタフェースを実装するオブジェクトに変換する処理は、サービスプロバイダ (プリンタ名からプリンタアドレス情報へのバインディングを保存)、プリンタサービスプロバイダ (PrinterFactory クラスと BSDPrinter クラスを提供)、JNDI SPI フレームワーク (前の二者を結び付けて、アプリケーションが直接使用できるオブジェクトを返す) が協同することにより透過的に行われます。

このようなオブジェクトのためのサービスプロバイダは、次のことを行わなければなりません。

  1. Referenceable を実装する、あるいは Reference のサブクラスであるオブジェクト (例: BSDPrinter) のクラスを定義します。
  2. Reference と、オブジェクトの参照アドレスを定義します。
  3. ObjectFactory (例: PrinterFactory) を実装するファクトリクラスを定義します。このクラスの getObjectInstance() メソッドは、ステップ 2 で定義された Reference を与えられるとステップ 1 で定義されたクラス (例: BSDPrinter) のインスタンスを生成します。

4.1.2 URL 参照の取り扱い

Reference に、URL タイプのアドレスは含まれるがファクトリのクラス名と位置は含まれない場合、またはその参照が URL を含む文字列の配列である場合は、JNDI は節 3.2 で説明した URL コンテキストファクトリサポートを使ってファクトリを探し、次にアドレス内の URL 文字列をファクトリの getObjectInstance() メソッドに渡します。JNDI が予期する URL コンテキストファクトリの実装の動作については、節 4.1.6 で説明します。

このようなオブジェクトのためのサービスプロバイダは、次のことを行わなければなりません。

  1. オブジェクト (例: BSDPrinter) のクラスを定義します。
  2. オブジェクトの URL スキーマを定義します。
  3. ObjectFactory を実装する URL コンテキストファクトリを定義します。このクラスの getObjectInstance() メソッドは、ステップ 2 で定義された URL を与えられるとステップ 1 で定義されたクラス (例: BSDPrinter) のインスタンスを生成します。

4.1.3 任意参照の取り扱い: java.naming.factory.object プロパティ

Reference からのファクトリ情報の抽出または URL の使用に加え、JNDI は java.naming.factory.object プロパティで指定されているオブジェクトファクトリを探すこともできます。 このプロパティは環境内またはプロバイダリソースファイルにあります (節 2.9.5 を参照)。このプロパティには、オブジェクトファクトリの完全修飾クラス名のコロンで区切られたリストがあります。各クラスは ObjectFactory インタフェースを実装し、引数をとらない public のコンストラクタを持たなければなりません。リスト内の各クラスに対し、JNDI はファクトリクラスのロードとインスタンス生成を試み、与えられた環境引数とオブジェクトを使ってそのインスタンス上で ObjectFactory/DirObjectFactory.getObjectInstance() メソッドの呼び出しを試みます。生成に成功すると、結果のオブジェクトが返されます。 失敗した場合は、JNDI はリスト内の次のクラスに同じ手順を使い、リストが終わるかファクトリが null 以外の結果を返すまでこれを繰り返します。

図 4: java.naming.factory.object を使って名前空間からオブジェクトを取り戻す例

java.naming.factory.object を使って名前空間からオブジェクトを取り戻す例

プリンタの例では、名前空間内のプリンタを表現するために Reference を使うのではなく、他の情報が保存されます。その情報があとで取得されると、その情報を Printer インスタンスに変換する試みの次に、java.naming.factory.object で指定されたオブジェクトファクトリが試みられます。

このようなオブジェクトのためのサービスプロバイダは、次のことを行わなければなりません。

  1. オブジェクト (例: BSDPrinter) のクラスを定義します。
  2. オブジェクトの参照情報のためのクラスを定義します。これは、名前空間内でバインドされるオブジェクトです。これは、Reference である必要はありません。対応するオブジェクトファクトリが理解できるものなら何でもかまいません (例: サーバ名「printer type=bsd; host=lobby-printserver」を含む文字列)。
  3. ObjectFactory (例: PrinterFactory) を実装するファクトリクラスを定義します。このクラスの getObjectInstance() メソッドは、ステップ 2 で定義されたクラス (例: 「printer type=bsd; host=lobby-printserver」) のインスタンスを与えられるとステップ 1 で定義されたクラス (例: BSDPrinter) のインスタンスを生成します。

サービスプロバイダは、オブジェクトをバインドまたはルックアップするときに、自動的に実際のオブジェクト (例: BSDPrinter) と参照情報 (ステップ 2、例: 「printer type=bsd; host=lobby-printserver」) の間で変換を行うものとします。

アプリケーションでオブジェクトの生成に特別なファクトリを使いたい場合は、アプリケーションの java.naming.factory.object 環境プロパティにそのファクトリのクラス名を入れ、そのファクトリのクラスとオブジェクトクラスを準備しなければなりません。

4.1.4 デフォルト動作のオーバーライド

オブジェクトファクトリ「ビルダ」は、オブジェクトファクトリのインスタンスを生成するクラスです。

アプリケーションは、オブジェクトファクトリの実装を探して構築する方法についての独自のポリシーを定義するために、オブジェクトファクトリビルダをインストールすることができます。ビルダがインストールされている場合は、オブジェクトファクトリの生成の責任をビルダが単独で負います。通常 JNDI が使用するデフォルトのポリシー (Reference、URL 文字列、java.naming.factory.object プロパティ) は採用されません。

図 5: ファクトリビルダを使って名前空間からオブジェクトを取り戻す例

オブジェクトファクトリビルダを使って名前空間からオブジェクトを取り戻す例

オブジェクトファクトリビルダのためのサービスプロバイダは、次のことを行わなければなりません。

  1. ObjectFactory を実装するオブジェクトファクトリを定義します。
  2. ObjectFactoryBuilder を実装するクラスを定義します。このクラスの createObjectFactory() メソッドは、ステップ 1 の ObjectFactory クラスのためのコンストラクタを使用します。

このファクトリビルダを使用するアプリケーションは、これをインストールしなければなりません。

	NamingManager.setObjectFactoryBuilder(builder); 

4.1.5 コンテキストファクトリ

「コンテキストファクトリ」は Context のインスタンスを生成するオブジェクトファクトリです。特定のネームサービスまたはディレクトリサービスのためのこれらのコンテキストの実装を、「コンテキスト実装」と呼びます。コンテキスト実装については、第 2 章で説明しました。他のオブジェクトファクトリと同様に、コンテキストファクトリは、すでに説明した 3 つの機構 (Reference、URL スキーマ ID、および java.naming.factory.object プロパティ内のリスト) のどれを使っても取得できます。

4.1.6 URL コンテキストファクトリ

URL コンテキストファクトリは、特殊なコンテキストファクトリです。このファクトリは、ObjectFactory.getObjectInstance() を実装するとき、次の規則に従います。

getObjectInstance(null, null, null, env) 
getObjectInstance("ldap://ldap.wiz.com/o=wiz,c=us", null, null, env);

4.2 オブジェクトの保存: 状態ファクトリ

JNDI には、基本的なコンテキスト実装による保存が可能な形式にオブジェクトを変換するための機構があります。その形式は、基本的なコンテキスト実装がアクセス可能ならどんなタイプでもかまいません。たとえば、Reference、URL、Serializable オブジェクト、属性セットなど、基本的なコンテキスト実装で許容されるデータです。任意のオブジェクトを名前空間に保存できるデータに変換することは、「状態ファクトリ」の使用によりサポートされます。状態ファクトリは、StateFactory インタフェース (または DirStateFactory サブインタフェース) を実装するクラスです。

	public interface StateFactory {	
		public Object getStateToBind(Object obj, 
					     Name name,
					     Context nameCtx, 
					     Hashtable env)
			throws NamingException;
	}

	public interface DirStateFactory {
		public DirStateFactory.Result getStateToBind(Object obj, 
							     Name name,
							     Context nameCtx, 
							     Hashtable env,
							     Attributes attrs)
			throws NamingException;
	}

オブジェクト (obj)、オブジェクトの名前とどこにバインドするかに関するオプションの情報、および追加の環境情報 (たとえば、名前空間をアクセスするユーザについての識別または認証情報) を与えられると、ファクトリはバインディングに適したオブジェクトの生成を試みます。普通、状態ファクトリは目的のネームサービスまたはディレクトリサービスとコンテキスト実装の両方またはどちらかについての知識を持ち、どのデータ形式が許容されるかを認識しています。DirContext 実装に使用される状態ファクトリの場合は、ファクトリにはオブジェクトとともに保存されているいくつかの属性も与えられます。ファクトリがオブジェクトに関してさらに多くの情報を必要とする場合は、name/nameCtx 引数を使ってネームサービスまたはディレクトリサービスから直接取得することができます。たとえば、LDAP ディレクトリのためのプリンタ状態ファクトリは、プリンタを表す属性セットを返すことができます。

与えられた引数を使ってデータを返すことができない場合は、ファクトリは null を返すものとします。たとえば、プリンタ状態ファクトリにディスクオブジェクトが与えられたときは、null を返します。他の状態ファクトリが試されるべきではない場合、その状態ファクトリは例外をスローするだけです。したがって、ファクトリはその実装からスローされる例外に留意すべきです。たとえば、プリンタ状態ファクトリにプリンタオブジェクトが与えられたが属性が矛盾する場合には、ファクトリは例外をスローします。

4.2.1 入出力オプション

最終的に、ファクトリの出力形式は基本的なネームサービスまたはディレクトリサービスによって決まります。たとえば、CORBA Object Services (COS) ネームサービスのためのコンテキスト実装がサービスに保存できるのは CORBA オブジェクト参照だけであり、LDAP のためのコンテキスト実装が保存できるのは属性だけです。 ただし、これらの属性内の情報の符号化には多くの柔軟性があります。

サービスプロバイダは普通、予期される各 (共通) タイプの入力にそれぞれファクトリを提供し、アプリケーションはそれらのファクトリのセットを自分の状態ファクトリに拡張できます。たとえば、COS ネーミングのサービスプロバイダに、Java Remote Method Inocation (RMI) オブジェクトを CORBA オブジェクト参照に変換するための状態ファクトリがあるとします。そのプロバイダのユーザは、Microsoft COM オブジェクト参照を CORBA オブジェクト参照に変換するための状態ファクトリを追加できます。

4.2.2 状態ファクトリの検出: java.naming.factory.state プロパティ

JNDI は、java.naming.factory.state プロパティで指定された状態ファクトリを探します。 このプロパティは環境またはプロバイダリソースファイルにあります (節 2.9.5 を参照)。このプロパティには、状態ファクトリの完全修飾クラス名のコロンで区切られたリストがあります。各クラスは StateFactory インタフェースを実装し、引数をとらない public のコンストラクタを持たなければなりません。リスト内の各クラスに対し、JNDI はファクトリクラスのロードとインスタンス生成を試み、与えられたオブジェクト、名前、コンテキスト、環境、属性の引数を使って StateFactory/DirStateFactory.getStateToBind() メソッドの呼び出しを試みます。ファクトリから生じる結果が null 以外の場合は、その結果が返されます。 それ以外の場合は、JNDI はリスト内の次のクラスに同じ手順を使い、リストが終わるかファクトリが null 以外の結果を返すまでこれを繰り返します。

4.3 LDAP v3 コントロールのナロー変換: 応答コントロールファクトリ

LDAP v3 プロトコルを使って、応答コントロールにサーバから送られた応答を添付することができます。コントロールは、OID 文字列 IDと ASN.1 BER で符号化されたバイトコード列からなります。外部の情報または補助がない場合は、コンテキスト実装が返せるのは Control インタフェースの簡単な実装だけで、これは OID とバイト列を返します。

JNDI には、応答コントロールを扱うための次の abstract クラスがあります。

	public abstract javax.naming.ldap.ControlFactory {
	...
	public static Control getControlInstance(Control ctl,
						Context ctx,
						Hashtable env)
		throws NamingException;
	public abstract Control getControlInstance(Control ctl)
		throws NamingException;
}

コンテキスト実装は応答コントロールを受け取ると、static の getControlInstance() メソッドを呼び出して、コントロールをよりユーザフレンドリーなアクセスメソッドにナロー変換できるコントロールファクトリを探します。そのようなコントロールは、たとえば ASN.1 BER のバイト列を復号化し、情報を Java タイプとして返せるメソッドへのアクセスを提供できます。このようなコントロールファクトリが見つからない場合は、元の応答コントロールが返されます。次は、時刻を復号化する仮想の TimeResponseControl の例です。

public class TimeResponseControl implements Control {
	long time;
	// Constructor used by ControlFactory
	public TimeResponseControl(String OID, byte[] berVal)
		throws NamingException {
		// check validity of OID
		time = // extract time from berVal
	};

	// Type-safe and User-friendly method
	public long getTime() {
		return time;
	}
	// Low-level methods
	public String getID() {
		return TIME_OID;
	}
	public byte[] getEncodedValue() {
		return // original berVal
	}
	 ...
}

コントロールファクトリが責任を持つコントロールの数は 1 つでも複数でもかまいません。与えられた引数を使ってコントロールを返すことができない場合、ファクトリは null を返すものとします。普通、コントロールファクトリは、コントロールの OID をファクトリがサポートする OID のリストと単に照合するだけです。他のコントロールファクトリを試みるべきではない場合にのみ、コントロールファクトリは例外をスローするものとします。したがって、ファクトリはその実装からスローされる例外に留意すべきです。たとえば、コントロールファクトリにそのファクトリがサポートする OID が与えられたが、バイト列に符号化の誤りがある場合には、例外をスローすべきです。

以下にコントロールファクトリの例を示します。

public class VendorXControlFactory extends ControlFactory {
	public VendorXControlFactory () {
	}
	public Control getControlInstance(Control orig) 
		throws NamingException {
		if (isOneOfMyControls(orig.getID())) {
			 ... 
			// determine which of ours it is and call its constructor
			return new TimeResponseControl(orig.getID(),
								orig.getEncodedValue());
		}
		return null;  // not one of ours
	}
}

4.3.1 応答コントロールファクトリの検出: java.naming.factory.control プロパティ

JNDI は、java.naming.factory.control プロパティで指定された応答コントロールファクトリを探します。 このファクトリは環境またはプロバイダリソースファイルにあります (節 2.9.5 を参照)。このプロパティには、コントロールファクトリの完全修飾クラス名のコロンで区切られたリストがあります。各クラスは ControlFactory インタフェースを実装し、引数をとらない public コンストラクタを持たなければなりません。リスト内の各クラスに対し、JNDI はファクトリクラスのロードとインスタンス生成を試み、与えられたコントロール、コンテキスト、環境の引数を使って ControlFactory.getControlInstance() インスタンスメソッドの呼び出しを試みます。ファクトリから生じる結果が null 以外の場合は、その結果が返されます。 それ以外の場合は、JNDI はリスト内の次のクラスに同じ手順を使い、リストが終わるかファクトリが null 以外の結果を返すまでこれを繰り返します。

4.4 パラメータの所有権

ファクトリ内のメソッドにパラメータとして渡されたオブジェクトは、すべて呼び出し側が所有します。したがってファクトリは、操作またはオブジェクトの修正の期間を超えてオブジェクトへのポインタを保持することを禁じられています。ファクトリがパラメータに含まれる情報を操作期間を超えて保存する必要がある場合は、パラメータのコピーを独自に保持するものとします。

4.5 再入可能性

ファクトリのインスタンスは再入可能とします。つまり、あるファクトリの単一のインスタンス上のメソッドを複数のスレッドが同時に呼び出すことができるものとします。



[先頭の項目] [前の項目] [次の項目] [最後の項目]

1 クラスの図の凡例については付録 B を参照してください。

2 CannotProceedException は、コンテキスの内部メソッドの 1 つが「処理中の名前がそのメソッドのネームシステムの範囲を越えていること」を検知したときにスローされていることがよくあります。例外をスローするプロセスは、コンテキストの実装に依存しています。

3 これはアプリケーション内のコードであることに注意してください。「連合内での操作の続行」のコード例は、コンテキスト実装内のコードです。

4 この目的に、Hashtable のサブクラス (Properties など) を使用することもできます。

5 このドキュメントで「URL」は、RFC 1738 とそれに関連する RFC により定義される URL 文字列を指します。URL 文字列は RFC の構文規則に準拠する任意の文字列です。対応するサポートが java.net.URL クラスまたは Web ブラウザに必ずあるとは限りません。URL 文字列は、String の名前パラメータまたは Name パラメータの最初の要素として渡されます。

jndi@java.sun.com
Copyright © 1999, Sun Microsystems, Inc. All rights reserved.