JNDI SPIは、開発者がさまざまなネームおよびディレクトリ・サービス・プロバイダを記述し、それを使用可能にすることによって、対応するサービスにJNDI APIを使用するアプリケーションがアクセスできるようにするための手段を提供します。 サービス・プロバイダは、共同でJNDI APIの要求を満たすモジュールのセットです。 また、JNDIでは複数の名前空間にまたがる名前(複数)の使用が許されるので、サービス・プロバイダの実装で1つの操作を行うために、別のサービス・プロバイダとの相互作用が必要な場合があります。 SPIには、複数の異なるプロバイダ実装が共同でクライアントのJNDI操作を行えるようにするためのメソッドがあります。
このドキュメントでは、SPIのコンポーネント、および開発者がJNDIのためのサービス・プロバイダを構築する方法について説明します。 読者はJNDI APIドキュメントの内容に習熟しているものと仮定します。
サービス・プロバイダの開発者は、必ずJNDI APIドキュメントの「セキュリティについて」を読んでください。 JNDIを使用する開発者、特にサービス・プロバイダの開発者が考慮すべき重要な内容が含まれています。
JNDI APIの下に位置する実装には複数のタイプがあります。 サービス・プロバイダには、最低1つのコンテキスト実装が含まれます。 コンテキスト実装は、Contextインタフェースまたはそのサブインタフェースのいずれか(DirContext、EventContext、LdapContextなど)を実装します。 実装の複雑さは、主に背後のサービスの複雑さに依存し、次にその実装がサポートするJNDIの機能の数に依存します。 第2章では、コンテキスト実装の構築の詳細について説明します。
コンテキスト実装には、様々な方法でアクセスできます。 もっとも一般的な方法は、初期コンテキストからアクセスする方法です。 第3章では、初期コンテキストからコンテキスト実装にアクセスする2つの方法(初期コンテキスト・ファクトリから行う方法と、URLコンテキスト・ファクトリを通じて行う方法)を説明します。
JNDIアーキテクチャは、コンテキスト実装の動作を強化するために使用できるコンポーネントまたは実装を定義します。 これにより、ユーザーとアプリケーションは実装をカスタマイズできます。 これらのコンポーネントは、ファクトリを通してサポートされます。 JNDIには3種類のファクトリが定義され、それらを利用するためのSPIメソッドがあります。 これらのファクトリについては、第4章に説明されています。
JNDI SPIは、javax.naming.spiパッケージに含まれています。 ここでは、SPIの概要について説明します。 SPIの詳細は、対応するjavadocドキュメントを参照してください。
この図の説明は、APIドキュメントに記載されています。
NamingManagerクラスには、プロバイダ関連の操作を実行するstaticメソッドが含まれています。 たとえば、Referenceを使用してオブジェクトのインスタンスを作成するメソッド、java.naming.factory.initialプロパティを使用して初期コンテキストのインスタンスを取得するメソッド、ObjectFactoryBuilderやInitialContextFactoryBuilderをインストールするメソッドなどが含まれています。 DirectoryManagerクラスは、DirContext関連の操作のための類似したstaticメソッドを提供します。
InitialContextFactoryは、初期コンテキスト・インスタンスを作成するためのインタフェースです。 詳細は、セクション3.1を参照してください。
InitialContextFactoryBuilderは、InitialContextFactoryインスタンスを作成するためのインタフェースです。 詳細は、セクション3.3を参照してください。
ObjectFactoryは、名前空間に格納されている情報を使用したオブジェクトの作成をサポートするためのインタフェースです。 DirObjectFactoryは、DirContextインタフェースを実装するコンテキスト実装で使用するためのObjectFactoryのサブインタフェースです。 詳細は、セクション4.1を参照してください。
ObjectFactoryBuilderは、オブジェクト・ファクトリを作成するためのインタフェースです。 詳細は、セクション4.1.4を参照してください。
StateFactoryは、ネーム・サービスまたはディレクトリ・サービスによってサポートされる格納可能な形式へのオブジェクトの変換をサポートするためのインタフェースです。 DirStateFactoryは、DirContextインタフェースを実装するコンテキスト実装で使用するためのStateFactoryのサブインタフェースです。 DirStateFactory.Resultは、DirStateFactory.getStateToBind()によって返されたjava.lang.ObjectとAttributesのペアを保持するためのクラスです。 詳細は、セクション4.2を参照してください。
Resolverインタフェースは、プロバイダがContextへの拡張インタフェースをサポートするための連合に参加できるように実装するメソッドを定義します。 詳細は、10ページの「コンテキストのサブインタフェース経由の解決」を参照してください。
ResolveResultは、Resolver.resolveToClass()の呼出しの戻り値です。 解釈に成功したオブジェクトと、未解釈の名前が含まれます。
サービス・プロバイダ構築の基本作業の1つは、Contextインタフェースまたはそのサブインタフェースのいずれかを実装するクラスを定義することです。 このクラスは、コンテキスト実装と呼ばれます。 コンテキスト実装の開発には、次のガイドラインを参照してください。
一般に、Contextインタフェース(またはサブインタフェース)内のメソッドおよびNamingManagerまたはDirectoryManagerユーティリティ・メソッドにパラメータとして渡されるオブジェクトはすべて、呼出し側によって所有されます。 多くの場合、パラメータは最終的にコンテキスト実装に到達します。 呼出し側がオブジェクトを所有しているため、コンテキスト実装がそのオブジェクトを修正することは禁じられています。 さらに、コンテキスト実装がそのオブジェクトへのポインタを保持できるのは操作の間のみで、それ以降のポインタの保持は許可されません。 コンテキスト実装がパラメータに含まれている情報を操作の期間を超えて保存する必要がある場合は、独自のコピーを保持するべきです。
パラメータの所有権の目的から、コンテキスト・インスタンス上の操作は、その操作によって生成されたいずれかの照会がまだ追跡されている間か、またはその操作によってNamingEnumerationが返された場合はその列挙がまだ使用されている間は、完了したとは見なされません。
コンテキスト・インスタンスは、再入可能である必要はありません。 同じコンテキスト・インスタンスを同時にアクセスする必要のある2つのスレッドは、互いに同期して必要なロックを行うものとします。
ただし、異なるコンテキスト・インスタンスは、並行マルチスレッド・アクセスに対して安全である必要があります。 つまり、それぞれのコンテキスト・インスタンスに同時に操作を行う2つのスレッドは、互いにアクセスを同期する必要があってはなりません。 たとえば、2つのコンテキストが同じリソース(同じ接続など)を共有している場合でも、2つの別々のスレッドが明示的な同期を行わなくてもそれぞれのコンテキストでの処理が可能(かつ安全)でなければなりません。
並行性の制御の目的から、コンテキスト・インスタンス上の操作は、その操作によって生成されたいずれかの照会がまだ追跡されている間か、またはその操作によってNamingEnumerationが返された場合はその列挙がまだ使用されている間は、完了したとは見なされません。
コンテキスト実装は、その実装がサポートするContextインタフェースまたはサブインタフェース内の各メソッドのための実装を定義します。
メソッドがサポートされていない場合は、OperationNotSupportedExceptionをスローするものとします。
名前引数を(StringまたはNameとして)受け入れるContextインタフェースまたはサブインタフェース内のメソッドの場合、空の名前は現在のコンテキストを示します。 たとえば、lookup()に空の名前が指定されている場合は、現在のコンテキストの新しいインスタンスが返されます。 list()に空の名前が指定されている場合は、現在のコンテキスト内の名前が列挙されます。 getAttributes()に空の名前が指定されている場合は、このコンテキストに関連付けられた属性が取得されます。
付録Aには、フラットなインメモリー名前空間を実装するコンテキスト実装の例が含まれています。
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章に説明されています。
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はオブジェクトの名前であり、nameCtxはnameを解決するときのコンテキストです。 name/nameCtxのペアを使用すると、そのオブジェクトに関する、refInfoから取得できるより多くの情報を取得できます。envは、getObjectInstance()呼出し元のコンテキストの環境です。attrsは、そのオブジェクトに関するディレクトリから読み取られた属性のコレクションであり、通常はrefInfoを取得するために使用されるのと同じ要求内にあります。 完全なコレクションが要求されなかった場合は、属性の完全なコレクションでないことがあります。
NamingManagerクラス内のメソッドがContextインタフェースを実装するコンテキスト実装によって使用されるべきであるのに対して、DirectoryManagerクラス内のメソッドは、DirContextインタフェースを実装するコンテキスト実装によって使用されるべきです。
次のメソッドに返すオブジェクトを構築する場合、コンテキスト実装はgetObjectInstance()か、またはバインドされた情報からオブジェクトを生成するための独自のメカニズム(この機能をそのコンテキスト内で有効にする場合)を呼び出すようにしてください。 (文字列のオーバーロードは示されていません。)
javax.naming.Context.lookup(Name name)
javax.naming.Context.lookupLink(Name name)
javax.naming.Binding.getObject()
javax.naming.directory.SearchResult.getObject()
BindingとSearchResultの場合、コンテキスト実装は、getObjectInstance()またはそれと同等のメソッドの呼出しの結果であるオブジェクトをコンストラクタに渡すか、またはそのgetObject()実装が戻る前にgetObjectInstance()またはそれと同等のメソッドを呼び出すようにBindingとSearchResultのデフォルトの実装をオーバーライドするようにしてください。
次に例を示します。 プリンタが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);
}
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はオブジェクトの名前であり、nameCtxはnameを解決するときのコンテキストです。 name/nameCtxのペアを使用すると、そのオブジェクトに関する、objから取得できるより多くの情報を取得できます。envは、getStateToBind()の呼出し元のコンテキストの環境です。attrsは、オブジェクトにバインドされる属性のコレクションです。 DirStateFactory.Resultは、オブジェクトおよび属性のコレクションを含むクラスです。
NamingManagerクラス内のメソッドがContextインタフェースを実装するコンテキスト実装によって使用されるべきであるのに対して、DirectoryManagerクラス内のメソッドは、DirContextインタフェースを実装するコンテキスト実装によって使用されるべきです。
アプリケーションによって指定されたオブジェクトを格納する前に、コンテキスト実装は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 {
...
}
これらの例に示されているように、コンテキスト実装は、さまざまな型のオブジェクト(Reference、Serializable、およびDirContext)を格納できる場合があります。 コンテキスト実装がReferenceableオブジェクトを直接格納できず、getStateToBind()がこのようなオブジェクトを返した場合、コンテキスト実装は、代わりにあとでReferenceable.getReference()を呼び出し、結果として得られたReferenceを格納するものとします。
コンテキスト実装がさまざまな型のオブジェクトを格納できる場合は、次の一般的な型について、次の順序に従うようにしてください。
この順序を推奨する理由は、bind()/rebind()メソッドの呼出し側の意図をもっとも的確に示す順序と考えられるからです。 たとえば、ReferenceはSerializableであるため、最初にSerializableチェックを実行した場合は、Referenceオブジェクトが参照形式で格納されることはなくなります(つまり、すべて直列化されます)。
コンテキストに文字列名の引数が与えられたとき、その名前は複数の名前空間にまたがる可能性のある合成名を表すか、または1つの名前空間に属する1つの複合名コンポーネント(これがさらに1つまたは複数の基本名で構成されている可能性があります)のみを含んでいる可能性があります。 コンテキスト実装は名前のどの部分をコンテキスト内で解釈または処理するかを判断し、残りを次のコンテキストに渡さなければなりません。 これは、構文解析的に名前を調べて行なっても、動的に名前を解釈して行なってもかまいません。
コンテキストにName引数が与えられたとき、それがCompositeNameのインスタンスである場合は、合成名として処理されます。 それ以外の場合は、CompoundNameクラスまたはその他の何らかの複合名実装によって実装される複合名として処理されます。
コンテキストは、すべてのコンテキスト操作の解決フェーズを実行することによって、連合に参加します。 lookup()メソッドは、常にサポートされる必要があります。 その他のメソッドのサポートは任意ですが、連合に参加するコンテキストの場合は、すべての操作で暗黙の解決をサポートしなければなりません。
図1: bind()を実行するための中間コンテキスト経由の解決の例。
たとえば、コンテキストがbind()操作をサポートしていないとします。 そのコンテキストがbind()のための中間コンテキストとして使用されるとき、その操作を次のコンテキストに続行できるようにするには、このコンテキストがその操作の解決の部分を実行する必要があります。 自身のコンテキストでバインディングを作成するよう要求されている場合は、OperationNotSupportedExceptionをスローするだけです。 図1は、bind()操作を目的のコンテキストで実行するために中間コンテキストを通じて渡す方法を示します。
DirContextメソッド(getAttributes()など)を呼び出すには、アプリケーションはまず初期のDirContextを取得し、次にそのDirContext上で操作を実行します。
DirContext ctx = new InitialDirContext(); Attributes attrs = ctx.getAttributes(someName);コンテキスト実装の観点から見ると、属性を取得するために、
getAttributes()が複数のネーミング・システムをたどることが必要になる場合があります。 これらのネーミング・システムの一部では、DirContextインタフェースはサポートされず、Contextインタフェースのみがサポートされています。 これらのネーミング・システムは、ターゲットのコンテキストに向けての解釈のための中間的存在として使用されます。 ターゲット・コンテキストは、DirContextインタフェースをサポートする必要があります。 この例を図2に示します。
図2: 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;
}
(マルチコンポーネント)合成名の解決は、通常は対応するコンテキスト実装によって処理される各ネーミング・システムにまたがるコンポーネントを解決しながら、あるネーミング・システムから次のネーミング・システムへと進められます。 コンテキスト実装の観点から見ると、コンテキスト実装は、自身が担当していないコンポーネントを次のネーミング・システム (のコンテキスト実装)に渡します。
次のネーミング・システムのためのコンテキスト実装を見つけるための方法はいくつかのあります。 あるネーミング・システム内の名前が次のネーミング・システム内のコンテキスト(またはコンテキストへの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コンテキストが指名されます。 次のネーミング・システムをこの方法で見つける場合は、あるネーミング・システムから次のネーミング・システムへの境界を示すためにJNDIの合成名区切り文字が使用されます。これは、次のネーミング・システムの暗黙的ポインタと呼ばれます。
ただし、次のネーミング・システムが見つかったら、コンテキスト実装は、次のネーミング・システムに合成名の解決すべき残りの部分を渡す必要があります。
複数の名前空間にまたがる名前に対する操作を実行する場合、中間のネーミング・システム内のコンテキストは、その操作を次のネーミング・システムに渡す必要があります。 コンテキストは、これを行うためにまず、その操作がどこまで進んだかを正確に示す情報を含むCannotProceedExceptionを構築します。 この処理の中で、この例外の解決済のオブジェクト、解決済の名前、残りの名前、および環境の部分を設定します。2 (Context.rename()メソッドの場合は、「resolved newname」の部分も設定します。)
次に、staticメソッドNamingManager.getContinuationContext()にCannotProceedExceptionを渡すことによって、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;
...
}
継続コンテキストを取得したら、名前の未解釈の残り部分を使って操作を続行します。
たとえば、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()に渡されます。 操作を続行できない場合、継続コンテキストは、元のbind()操作の呼出し側にCannotProceedExceptionをスローします。
連合の構成によっては、あるネーミング・システムでの解決の結果が、どれが次のネーミング・システムであるかを示さない場合があります。 コンテキスト実装に得られる結論は、「解釈は現在のネーミング・システムで終わったが、次のネーミング・システムに進まなければならない」ことです。
たとえば、ZIP形式のファイルを指定する「lib/xyz.zip」と、そのZIPファイル内のエントリを指定する「part1/abc」の2つの部分で構成された合成名「lib/xyz.zip/part1/abc」があるとします。" 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を構築します。 空白要素は次のネーミング・システムへの暗示的なポインタとして使用され、次のネーミング・システムへの位置まで解釈が進んだことを示します。 解釈済みのオブジェクトと解釈済みの名前の値がどのように対応するかに留意してください。 コンテキスト実装は次に、CannotProceedExceptionをgetContinuationContext()に渡します。
CannotProceedException内の任意の解決済みのオブジェクトと同様に、getContinuationContext()は、このnns参照を受け入れるコンテキスト実装を検索します。 たとえばZIPファイルのコンテキスト実装は、nns参照と、ファイルの名前(与えられたコンテキストに対する相対名)などの情報を受け取ります。 コンテキスト実装は、そのファイルがZIPファイルであると判断すると、そのファイル内の名前を解釈するためのコンテキストを生成します。
連合のためのJNDI SPIのフレームワークの中心は、CannotProceedExceptionです。 Cannot-ProceedExceptionには、NamingExceptionスーパー・クラスから継承された、解決済みの名前/オブジェクトや残りの名前などの情報が含まれています。 さらに、CannotProceedExceptionには、「代替」名や「代替」名コンテキストのためのフィールドも含まれます。 NamingExceptionから解釈された名前が完全な合成名(操作の最初のコンテキストに相対の名前)であるのに対し、代替名は代替名コンテキストに相対の解釈済み名です。 つまり、代替名は解釈済みの名前と同じでなくてもかまいません。 代替名と代替名コンテキストは、NamingManager/DirectoryManager.getObjectInstance()への引数として使用されます。 これらの引数により、このメソッドが呼び出したファクトリが解釈済みオブジェクトに関する詳細な情報を取得できます(たとえば、オブジェクトに関する特別な属性を取得するために使用するなど)。 これらのファクトリについては、第4章に説明されています。
JNDI SPIフレームワークでは「前方検索」が重視され、次のネーミング・システムの検出が試みられます。しかし、コンテキスト実装によっては、検出された後に解釈のチェーンを「後ろ向きに」たどってコンテキスト上の情報を得ることが必要です。 たとえば、ホスト・ネーミング・システムの連合から外された特定のコンテキスト実装が、「そのコンテキスト実装がホスト情報を見つける唯一の方法は、自分の上位(直接の上位ではない可能性がある)のネーミング・システムに問い合せることである」ような設計である可能性があります。 それを行うには、コンテキスト情報、つまり解決がその現在のポイントまでどのように進んできたかに関する情報が必要です。
連合についてのこれまでの説明をまとめると、次のようになります。複数の名前空間にまたがる名前に対する操作を実行する場合、コンテキスト実装はまず、その操作がどこまで進んだかを正確に示す情報を含むCannotProceed-Exceptionを構築します。 次に、getContinuationContext()を呼び出すことによって、JNDIから継続コンテキストを取得します。 コンテキスト情報の取得をサポートするために、getContinuationContext()はjava.naming.spi.CannotProceedException環境プロパティを、Cannot-ProceedException引数の値とともに継続コンテキストの環境に自動的に追加します。 このプロパティは継続コンテキストによって継承され、そのコンテキストの実装が例外のフィールドを調べるために使用されます。
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の実装に負担がかかります。 再度呼び出される操作に引数が余分に渡される場合があります。 照会コンテキストの実装は、余分な情報や不必要な情報を無視してもかまいません。
操作から、照会に加えて結果が返される場合があります。 たとえば、コンテキストを探しているとき、いくつかの照会に加えて、どこで詳細結果が得られるかに関する複数の結果をサーバーが返すことがあります。 これらの結果と照会は、プロトコル・レベルでインタリーブされる場合があります。 照会がユーザーの介入を必要とする(つまり、自動的には追跡されない)場合は、最初に検索の列挙を通じて結果を返すものとします。 結果が返されると、照会例外をスローできます。 これにより、シンプルなプログラミング・モデルを使って、照会とその結果セットとの間の明確な関係をユーザーに示すことができます。
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()は、指定されたオブジェクトのスキーマ・クラス定義を含むコンテキストを返します。 一部のシステムにはグローバルなスキーマが1つしかないため、name引数の値には関係なく、同じスキーマ・ツリーを返します。 きめ細かなスキーマ定義をサポートするシステムでは、調査されるコンテキストによって異なるスキーマ・ツリーを返します。
コンテキスト実装は、EventContext/EventDirContextインタフェース内のメソッドのための実装を提供することによってイベント通知をサポートします。 これらのインタフェースにより提唱されるイベント・モデルは、マルチスレッド・モデルを使って簡単にサポートできます。 アプリケーションがaddNamingListener()を使用してコンテキストにリスナーを登録すると、そのコンテキストは要求を記録し、イベントを生成するために必要な情報を収集するための処理を開始します。 コンテキストはイベントを生成するための情報を受け取ると、そのイベントをリスナーにただちに渡します。 普通、登録を行うスレッドはリスナーを実行するスレッドとは異なります。 また普通、コンテキスト実装は自分が作成したスレッドを使用し、リスナー・メソッドの実行を管理します。 1つのイベントが複数のリスナーにディスパッチされた場合は、コンテキスト実装はリスナー・メソッドを同時に別々のスレッドで実行することを選択できます(一般的にはこれを推奨)。
addNamingListener()メソッドは、NamingListenerのインスタンスを受け入れます。 このインスタンスには、NamingListenerの1つ以上のサブインタフェースが実装されていることがあります。 リスナーが複数のサブインタフェースを実装している場合は、コンテキスト実装は、登録を満たすために必要なリソースの保存を試みるものとします。 たとえば、1つの実装が複数のサブインタフェースの要求をすべて捕らえる単一の要求をサーバーに発行します。
コンテキストがさらにイベントを発生させることができない場合には、可能であれば、コンテキスト実装はリスナーに対してNamingExceptionEventを発生させ、自動的にそのリスナーの登録を取り消すことができるものとします。 たとえば、サーバーへの接続がリスナーの登録のあとで切断され、イベントを渡すための情報が利用できない場合には、コンテキストはNamingExceptionEventをリスナーにただちに渡します。
Context (またはそのサブインタフェース)の各インスタンスには、そのコンテキストが提供するサービスへのアクセス方法の、アプリケーションによって表される設定を含む環境を関連付けることができます。 環境内にある情報の例には、ユーザーの資格と望まれるセキュリティ・レベル(none、simple、strong)や構成情報(使用するサーバーなど)を指定するセキュリティ関連情報があります。 環境プロパティの詳細は、JNDI APIドキュメントの第6章および付録Aを参照してください。
環境プロパティは一般に、最大の移植性を保証するために定義されます。 個々のサービス・プロバイダはこれらの一般プロパティを、そのサービスに適応した特性にマップします。 プロバイダに関係のないプロパティは記録するにとどめて、無視します。 サービス・プロバイダに固有のプロパティまたはプリファレンスの、異なるプロバイダにまたがる適用性が制限されている場合には、それらのプロパティまたはプリファレンスを保存するためにも環境を使用できます。
環境プロパティが指定される方法については、JNDI APIドキュメントのセクション6.1を参照してください。 サービス・プロバイダに固有のプロパティには、プロバイダに対する一意性を反映する接頭辞を付けます。 サービス・プロバイダのパッケージ名を先頭に付ける手法がよく使われます。 たとえば、SunのLDAPプロバイダは主にcom.sun.jndi.ldapパッケージに含まれているため、SunのLDAPプロバイダに固有のプロパティには接頭辞「com.sun.jndi.ldap.」が付きます。
初期コンテキストを(InitialContextまたはそのサブクラスのコンストラクタを使用して)作成する場合、アプリケーションは環境をパラメータとして指定できます。 このパラメータは、Hashtableまたはそのサブクラスのいずれか(Propertiesなど)として表されます。 JNDIクラス・ライブラリは、このパラメータのデータをほかのソースからのデータで拡張し(JNDI APIドキュメントの第6章を参照)、これをコンテキスト実装に渡します。
その他のすべてのパラメータと同様に、コンテキスト実装によって受信された環境パラメータは、呼出し側によって所有されます。 コンテキスト実装は、受け取った環境パラメータのコピーを作成するか、「呼出し側がパラメータに加えた変更がコンテキスト実装から見えるものに影響しないこと、およびその逆を保証する」ステップをとるものとします。 また、環境パラメータがPropertiesインスタンスである場合は、パラメータの列挙およびHashtable.get()によって最上位のプロパティのみ(ネストされたデフォルトはすべて除く)が検査されることにも注意してください。 これは、予期される動作です。 コンテキスト実装がPropertiesインスタンスのネストされたデフォルト内の値を取得または列挙することは予期されません。
JNDIライブラリは、初期コンテキストへの環境パラメータ、リソース・ファイル、システム・プロパティやアプレット・パラメータ(該当する場合)などの、異なるソースからのプロパティをマージする役割を果たします(JNDI APIドキュメントの第6章を参照)。 コンテキスト実装は普通、与えられた環境から必要なプロパティだけを読み取ります。 コンテキスト実装がほかのソースを参考にする必要はほとんどありません。
環境は、コンテキスト・メソッドがあるコンテキストから次のコンテキストに進むときに、親から子に継承されます。 環境内の特定のプロパティが特定のコンテキストによって無視されるかどうかには関係なく、コンテキスト・インスタンスの環境全体が子コンテキスト・インスタンスによって継承されます。
環境のこの「継承」特性を実装するには、コンテキスト実装が、その環境をあるコンテキスト・インスタンスから次のコンテキスト・インスタンスに渡す必要があります。 1つのコンテキスト実装内では、環境を引数としてContextコンストラクタ、またはContextインスタンスを作成するためのNamingManager/DirectoryManager.getObjectInstance()メソッドに渡すことによって、それを行うことができます。
連合内の複数のコンテキスト実装にまたがる場合は、環境をNamingManager.getContinuationContext()/DirectoryManager.getContinuationDirContext()のCannotProceedExceptionパラメータの一部として渡すことによって、これがサポートされます。このメソッドがさらに、操作を続行するコンテキストのインスタンスを作成するときにこの環境を使用します。
継承は、「各コンテキストが環境の独自のビューを持つ」というセマンティックスが保持されているかぎり、どの方法でも実装できます。 たとえば、絶対必要になるまで環境のコピーを延期するために、copy-on-write実装を使うことができます。
コンテキストの環境は、Contextインタフェース内のaddToEnvironment() and 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を参照してください。
サービス・プロバイダにはそれぞれ、そのプロバイダに固有のプロパティを含むオプションのリソース・ファイルがあります。 このリソースの名前は次のようになります。
[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 で説明されているアルゴリズムと整合性のある方法でそれをマージする必要があります。
クライアント/サーバー・プロトコルを使用するコンテキスト実装の場合は、必ずしも、コンテキストとクライアント/サーバー間の接続の間に1対1のマッピングが存在しません。 JNDIは、接続を直接扱わない高レベルAPIです。 必要な接続管理はコンテキスト実装によって行われます。 そのため、複数のインスタンスで1つの接続を共有することもあり、またコンテキスト実装は独自のアルゴリズムを使って接続とネットワークの使い方を保存してもかまいません。 したがって、コンテキスト・インスタンスでメソッドが呼び出されるときに、コンテキスト実装は要求された操作を実行する以外に何らかの接続管理を行う必要がある可能性があります。
Context.close()およびNamingEnumeration.close()メソッドを使用すると、アプリケーションは、接続関連のリソースをいつ解放するかに関するヒントをコンテキスト実装に提供できます。 コンテキスト実装は、ガベージ・コレクションと接続関連リソースの保存のためのほかの手段を使うことを選択できます(一般的にはこれを推奨)。
一部の環境プロパティは、コンテキストの接続に影響を与えます。 たとえば、アプリケーションがセキュリティ関連のプロパティを変更すると、コンテキスト実装がそれらの更新されたプロパティを使って接続を修正したり新しく作成することが必要になる場合があります。 変更の前に、接続がほかのコンテキストと共有されていた場合は、プロパティが更新されていないコンテキストに接続の変更が影響してはいけません。
すべてのネーミング・メソッドがコンテキストを基準にして実行されるため、アプリケーションがこれらのネーミング・メソッドを呼び出すには開始コンテキストが必要です。 この開始コンテキストは、初期コンテキストと呼ばれます。 初期コンテキスト内のバインディングは、おそらく、グローバルな企業全体にわたる名前空間に名前を付けるための標準ポリシーを使用して、初期コンテキスト実装によって規定されるポリシーによって決定されます。 初期コンテキストには、たとえば、インターネット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クラス(およびサブクラス)は、初期コンテキスト・ファクトリ・ビルダー (下で説明)をインストールすることによってオーバーライドできるデフォルトのアルゴリズムを使用して実装を選択します。
InitialDirContextは、InitialContextの拡張です。 初期コンテキストを使ってディレクトリ操作を行うために使用されます。 InitialLdapContextクラスは、InitialDirContextの拡張です。 初期コンテキストを使って特別なLDAP v3操作を行うために使用されます。 ここで説明されているアルゴリズムとポリシーは、InitialDirContextとInitialLdapContextにも適用されます。 Contextの代わりにDirContext/LdapContextが必要な場所は説明に記載されています。
初期コンテキスト・ファクトリとは、第2章で概説されているガイドラインに従って実装されたコンテキストのインスタンスを作成するクラスのことです。 このファクトリは、InitialContextクラス(またはサブクラス)コンストラクタによって使用されます。
環境が与えられると、このファクトリは、Contextのインスタンス(またはそのサブインタフェース)を返します。
public interface InitialContextFactory {
public Context getInitialContext(Hashtable env)
throws NamingException;
}
付録Aには、InitialContextFactoryの例が含まれています。
コンテキスト・インスタンスが作成されたあと、URL以外の名前を使用してInitialContextでメソッドが呼び出されると(下を参照)、そのメソッドがそのコンテキスト・インスタンスに転送されて呼び出されます。
JNDIは、java.naming.factory.initialプロパティを使用して、使用する初期コンテキスト実装を選択します。 このプロパティには、1つの初期コンテキスト・ファクトリの完全修飾クラス名が含まれています。 このクラスは、InitialContextFactoryインタフェースを実装し、かつ引数を何も取らないpublicコンストラクタを備えている必要があります。 JNDIは、初期コンテキスト・ファクトリ・クラスをロードしてから、初期コンテキストとして使用されるContextインスタンスを取得するために、その上でgetInitialContext()を呼び出します。
特定の初期コンテキストを使用するアプリケーションは、InitialContext (またはサブクラス)コンストラクタに渡された環境で、またはリソース・ファイル、システム・プロパティ、アプレット・パラメータのいずれかを経由してjava.naming.factory.initialプロパティを指定する必要があります。
java.naming.factory.initialプロパティがnull以外の値に設定されている場合、InitialContext (およびサブクラス)コンストラクタは、初期コンテキスト・ファクトリをロードしてインスタンス化しようとします。次に、この初期コンテキスト・ファクトリによってコンテキスト・インスタンスが生成されます。 認証の問題などの原因で、ファクトリまたはコンテキストを作成できないときは、初期コンテキスト・ファクトリは例外をスローして、この問題を通知できます。 ただし、なんらかの環境プロパティまたは接続関連の問題を確認して初期コンテキストのユーザーに示す時期は、コンテキスト実装に依存することに注意してください。 問題の確認と通知は、コンテキストで操作が実行されるまで遅らせても、コンテキストが作成されたときにすぐに行ってもかまいません。
java.naming.factory.initialプロパティが設定されていない場合は、初期コンテキストに代わって背後のコンテキストを作成する処理は試行されません。 初期コンテキストは、たとえば次で説明するように、URL名の処理に使用し続けることができます。
URL5文字列が初期コンテキストに渡された場合、この文字列は、対応するURLコンテキスト実装を使用して解決されます。 この機能は、InitialContextクラス(およびサブクラス)によってサポートされ、java.naming.factory.initial環境プロパティの設定とは無関係です。
この機能を使用すると、アプリケーションは初期コンテキストを使用して、URLコンテキスト実装が使用可能になっている任意の名前空間に到達できます。 たとえば、次のコードは初期コンテキストから、あるLDAP名前空間のリストを作成します。
new InitialContext().list("ldap://lserver/ou=eng,o=wiz,c=us");
たとえば、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で拒否するものとします。
URLコンテキスト・ファクトリとは、1つ以上のスキームのURLのための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インタフェース(31ページの「URLコンテキスト・ファクトリ」を参照)を実装し、引数を取らないpublicコンストラクタを備えています。 InitialContextクラスがファクトリのgetObjectInstance()メソッドに解決済みのオブジェクトとしてスキームIDを渡すと、このメソッドがさらに、URLスキームのためのURLコンテキストを作成します。 このURLコンテキストが次に、InitialContextに指定されたURLで、最初に目的としたContextまたはDirContext操作を実行するために使用されます。
サービス・プロバイダがURLコンテキスト・ファクトリとURLコンテキスト実装を提供するという要件は存在しません。 それを行うのは、URL文字列名をそのURLスキームとともにInitialContextクラスで受け入れられるようにする場合だけです。 たとえば、サービス・プロバイダが、初期コンテキスト・ファクトリと、そのファクトリを通じてアクセスされるコンテキスト実装だけを提供してもかまいません。
java.naming.factory.initial環境プロパティとURLサポートを使用した初期コンテキスト・ファクトリの作成のポリシーは、InitialContextクラスに組み込まれています。 このポリシーのすべてまたは一部をアプリケーションがオーバーライドする方法は2つあります。
アプリケーションは、URL文字列を特別に処理したくない場合、NamingManager.getInitialContext()メソッドを使用できます。これにより、java.naming.factory.initial環境プロパティで指定されているファクトリを使用してコンテキスト・インスタンスが作成されます。
このメソッドは、アプリケーションが、初期コンテキスト・ファクトリが作成したコンテキストによって実装されたインタフェースにアクセスする必要があるが、それがContext、DirContext、LdapContextのいずれでもない場合にも役立ちます。 次に、NamingManager.getInitialContext()を使用してコンテキストを取得し、それをサブクラスにキャストするコード・フラグメントを示します。
FooContext ctx = (FooContext) NamingManager.getInitialContext(env); ... Object obj = ctx.lookup(name); ctx.fooMethod1(...);初期コンテキスト・ファクトリ・ビルダー(次に説明します)をインストールすると、
NamingManager.getInitialContext()の結果に影響を与えることに注意してください。
初期コンテキスト・ファクトリ・ビルダーとは、初期コンテキスト・ファクトリのインスタンスを作成するクラスのことです。
アプリケーションは、初期コンテキスト実装を検索して構築する方法についての独自のポリシーを定義するために、初期コンテキスト・ファクトリ・ビルダーをインストールできます。 ビルダーがインストールされている場合は、初期コンテキスト・ファクトリの作成の責任をビルダーが単独で負います。 通常JNDIによって使用されるデフォルト・ポリシー(java.naming.factory.initialプロパティまたはURLサポート)は、どれも使用されません。
初期コンテキスト・ファクトリ・ビルダーの実装は、InitialContext-FactoryBuilderインタフェースを実装する必要があります。 そのcreateInitialContextFactory()メソッドによって、InitialContextFactoryのインスタンスが生成されます。
ビルダーがインストールされたあと、アプリケーションはInitialContext/InitialDirContext/InitialLdapContextコンストラクタを使用して、またはNamingManager.getInitialContext()を使用して初期コンテキストを取得できます。 いずれかのコンストラクタが使用される場合、そのクラスは基本的に、NamingManager.getInitialContext()によって返された背後のコンテキスト実装のラッパーです。
Context、DirContext、またはLdapContextから拡張されたインタフェースをサポートする初期コンテキストを提供する必要がある場合、サービス・プロバイダは、InitialContext (またはInitialDirContext/InitialLdapContext)のサブクラスを指定する必要があります。
InitialContextやInitialDirContextの場合と同じ方法でURLのサポートを追加するには、サブクラスは、次のようにInitialContextで使用可能なprotectedメソッドを使用するものとします。 これが、名前の引数を受け取るメソッドを持つインタフェースにとって意味のある唯一の方法です。
たとえば、FooContextがDirContextのサブインタフェースであるとします。 その初期コンテキスト実装は、使用する実際の初期コンテキストを取得するgetURLOrDefaultInitFooCtx()メソッドを(NameとStringの両方のパラメータに対して)定義します。
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, ...);
}
名前引数がないか、またはURLサポートが必要ないFooContextインタフェース内の新しいメソッドの実装を提供する場合は、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);
}
実装は、このクラスのための適切なコンストラクタを提供するものとします。 コンストラクタは、スーパー・クラスの適切なコンストラクタを呼び出します。 スーパー・クラスのコンストラクタが呼び出される前に環境を変更または検査する必要がある場合は、初期コンテキストの初期化を制御するための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, ...);
JNDIでは、コンテキスト実装がネーム・サービスまたはディレクトリ・サービス内のオブジェクトを読み取ったり格納したりする方法を、アプリケーション、アプリケーションの開発者またはユーザー、あるいはサービス・プロバイダがカスタマイズできます。 LDAP v3コントロール・クラスのナロー変換についても、同様の機能を使用できます。
これらの機能は、コンテキスト実装にプラグインするモジュールと考えることができます。
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処理、および(プリンタの例で説明したように)データをアプリケーションが期待する形式に変換するために使用されます。
Referenceには、オブジェクト・ファクトリのクラス名と位置を返すためのメソッドが含まれています。 Referenceには、次のメソッドがあります。
public class Reference {
...
public String getClassName();
public String getFactoryClassName();
public String getFactoryClassLocation();
}
ディレクトリ・サービスまたはネーミング・サービスから読み取られたオブジェクトがReferenceまたはReferenceableのインスタンスである場合は、Reference内の情報を使用して、その対応するオブジェクト・ファクトリを見つけることができます。 getFactoryClassName()メソッドは、ObjectFactoryインタフェースを実装するファクトリ・クラスの名前を取得します。 このファクトリはObjectFactoryインタフェースを実装し、引数をとらないpublicコンストラクタを持たなければなりません。getFactoryClassLocation()は、ファクトリのためのクラス実装のコード・ベースを取得します。これは、空白で区切られたURLのリストです。
JNDIは、Referenceと環境を引数として使用して、ObjectFactoryインスタンスに対してgetObjectInstance()を呼び出すことによってオブジェクトを作成します。 その結果は、getClassName()で識別されるクラスのインスタンスです。
アプリケーションに返されたオブジェクトをインスタンス化するために必要なすべてのクラスが、JNDIによって提供されるメカニズムを使用して使用可能になることに注意してください。 アプリケーションは、ローカルでクラスをインストールする必要はありません。
図3: 参照を使用して名前空間からオブジェクトを取り戻す例
プリンタの例に戻り、Printerがプリンタを表すためのインタフェースであり、BSDPrinterクラスがそのインタフェースの実装であるとします。 BSDPrinterはReferenceableインタフェースを実装し、Referenceクラスを使用して、BSDPrinterのインスタンスを構築する方法に関する情報や印刷サーバーと通信するためのアドレス情報を格納します。 Referenceには、オブジェクトのクラス名(「Printer"」)、プリンタ・オブジェクト・ファクトリのクラス名(「PrinterFactory」)、およびファクトリのクラス実装をロードするためのURLが含まれています。 ファクトリ・クラス名と実装位置を使用して、JNDIはまずPrinterFactoryの実装をロードし、PrinterFactoryのインスタンスを作成します。 次に、ファクトリに対して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フレームワークの協力を通して透過的に実行されます。
このようなオブジェクトのためのサービス・プロバイダは、次のことを行う必要があります。
Referenceableを実装するか、またはReferenceのサブクラスであるオブジェクトのためのクラス(BSDPrinterなど)を定義します。Referenceとその参照アドレスを定義します。ObjectFactory (PrinterFactoryなど)を実装するファクトリ・クラスを定義します。 このクラスのgetObjectInstance()メソッドは、ステップ2のReferenceが与えられると、ステップ1のクラス(BSDPrinterなど)のインスタンスを生成します。 Referenceに「URL」タイプのアドレスが含まれるが、ファクトリのクラス名と位置が含まれていない場合や、その参照がURLを含む文字列の配列である場合、JNDIはセクション3.2で説明したURLコンテキスト・ファクトリのサポートを使用してファクトリを見つけたあと、アドレス内のURL文字列をファクトリのgetObjectInstance()メソッドに渡します。 JNDIが予測するURLコンテキスト・ファクトリの実装の動作については、セクション4.1.6を参照してください。
このようなオブジェクトのためのサービス・プロバイダは、次のことを行う必要があります。
BSDPrinterなど)のクラスを定義します。ObjectFactoryを実装するURLコンテキスト・ファクトリ・クラスを定義します。 このクラスのgetObjectInstance()メソッドは、ステップ2のURLが与えられると、ステップ1のクラス(BSDPrinterなど)のインスタンスを生成します。 Referenceからのファクトリ情報の抽出やURLの使用に加えて、JNDIは、java.naming.factory.objectプロパティで指定されているオブジェクト・ファクトリも探します。このプロパティは、環境内またはプロバイダ・リソース・ファイルにあります(セクション2.9.5を参照)。 このプロパティには、オブジェクト・ファクトリの完全修飾クラス名のコロンで区切られたリストがあります。 各クラスはObjectFactoryインタフェースを実装し、引数をとらないpublicコンストラクタを持たなければなりません。 リスト内のクラスごとに、JNDIはファクトリ・クラスをロードしてインスタンス化し、指定されたオブジェクトおよび環境引数を使用して、そのインスタンスに対してObjectFactory/DirObjectFactory.getObjectInstance()メソッドを呼び出そうとします。 生成が成功した場合は、結果として得られたオブジェクトが返されます。それ以外の場合は、リストが終わるか、またはファクトリがnull以外の結果を返すまで、JNDIはリスト内の次のクラスに対して同じ手順を実行します。
図4: java.naming.factory.objectを使用して名前空間からオブジェクトを取り戻す例
プリンタの例では、Referenceを使用して名前空間内のプリンタを表す代わりに、ほかの何らかの情報が格納されます。 その情報があとで取得されると、その情報をPrinterインスタンスに変換するために、java.naming.factory.objectが指定されたオブジェクト・ファクトリが順番に試行されます。
このようなオブジェクトのためのサービス・プロバイダは、次のことを行う必要があります。
BSDPrinterなど)のクラスを定義します。Referenceである必要はありません。 対応するオブジェクト・ファクトリで理解されるものであればなんでもかまいません(サーバー名「printer type=bsd; host=lobby-printserver」を含む何らかの文字列など)。 ObjectFactory (PrinterFactoryなど)を実装するファクトリ・クラスを定義します。 このクラスのgetObjectInstance()メソッドは、ステップ2のクラス(「printer type=bsd; host=lobby-printserver」など)のインスタンスが与えられると、ステップ1のクラス(BSDPrinterなど)のインスタンスを生成します。 サービス・プロバイダは、オブジェクトをバインドまたは検索するときに、実際のオブジェクト(BSDPrinterなど)と参照情報(ステップ2の「printer type=bsd; host=lobby-printserver」など)の間の変換を自動的に行うものとします。
オブジェクトを生成するために特定のファクトリを使用するアプリケーションは、そのjava.naming.factory.object環境プロパティにそのファクトリのクラス名を含め、そのファクトリのクラスとオブジェクト・クラスを使用可能にする必要があります。
オブジェクト・ファクトリ・ビルダーとは、オブジェクト・ファクトリのインスタンスを生成するクラスのことです。
アプリケーションは、オブジェクト・ファクトリの実装を検索して構築する方法についての独自のポリシーを定義するために、オブジェクト・ファクトリ・ビルダーをインストールできます。 ビルダーがインストールされている場合は、オブジェクト・ファクトリの生成の責任をビルダーが単独で負います。 通常JNDIによって使用されるデフォルト・ポリシー(Reference、URL文字列、またはjava.naming.factory.objectプロパティ)は、どれも使用されません。
図5: オブジェクト・ファクトリ・ビルダーを使用して名前空間からオブジェクトを取り戻す例
オブジェクト・ファクトリ・ビルダーのためのサービス・プロバイダは、次のことを行わなければなりません。
ObjectFactoryを実装するオブジェクト・ファクトリを定義します。ObjectFactoryBuilderを実装するクラスを定義します。 このクラスのcreateObjectFactory()メソッドは、ステップ1のObjectFactoryクラスのためのコンストラクタを使用します。 このファクトリ・ビルダーを使用するアプリケーションは、まずこれをインストールする必要があります。
NamingManager.setObjectFactoryBuilder(builder);
コンテキスト・ファクトリとは、Contextのインスタンスを作成するオブジェクト・ファクトリのことです。 特定のネーム・サービスまたはディレクトリ・サービスのためのこれらのコンテキストの実装は、コンテキスト実装と呼ばれます。 コンテキスト実装については、第2章で説明されています。 ほかのすべてのオブジェクト・ファクトリと同様に、コンテキスト・ファクトリは、上で説明されている3つのメカニズム(Reference、URLスキームID、またはjava.naming.factory.objectプロパティのリスト)のどれを使用しても取得できます。
URLコンテキスト・ファクトリは、特殊な種類のコンテキスト・ファクトリです。 これは、ObjectFactory.getObjectInstance()を実装するときに、次の規則に従います。
getObjectInstance(null, null, null, env)
ldap://ldap.wiz.com/o=wiz,c=us」または「ldap://ldap.umich.edu/」など).を解決できるコンテキストが返されます。refObjがURL文字列である場合は、URLで識別されるオブジェクトを生成します。 たとえば、
getObjectInstance("ldap://ldap.wiz.com/o=wiz,c=us", null, null, env);
ldap.wiz.com上の「o=wiz,c=us」で指定されたオブジェクトが返されます。 これがコンテキストを指定する場合は、相対的なLDAP名(「cn=Jane Smith」など)を解決するために使用できます。 refObjがURL文字列の配列である場合、URLは、それらが参照するコンテキストという点で同等であると仮定します。 URLが等しいこと、または等しい必要があることの確認はコンテキスト・ファクトリに任されています。 URLの配列内の順序は重要ではありません。 getObjectInstance()によって返されるオブジェクトは1つのURLの場合と同じであり、そのURLで指定されたオブジェクト(通常はコンテキスト)です。 refObjがそれ以外の型である場合、getObjectInstance()の動作は実装によって決定されます。
URLコンテキスト・ファクトリは、解決するURLが渡されたときに、InitialContextクラスによって使用されます。 URLコンテキスト・ファクトリはまた、名前空間に格納されているURLからJavaプログラミング言語でのオブジェクトを作成するためにも使用されます(セクション4.1.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)、そのオブジェクトの名前とバインドされている場所に関するオプション情報、および必要に応じて何らかの追加の環境情報(名前空間にアクセスしているユーザーに関する何らかのIDまたは認証情報など)が与えられると、ファクトリはバインディングに適したオブジェクトの生成を試みます。 普通、状態ファクトリは目的のネーム・サービスまたはディレクトリ・サービスとコンテキスト実装の両方またはどちらかについての知識を持ち、どのデータ形式が許容されるかを認識しています。 DirContext実装で使用される状態ファクトリの場合、そのファクトリには、オブジェクトとともに格納される一部の属性も与えられます。 オブジェクトに関するより多くの情報がファクトリに必要な場合、そのファクトリは、name/nameCtx引数を使用してネーム・サービスまたはディレクトリ・サービスからそれらを直接取得できます。 たとえば、LDAPディレクトリのためのプリンタ状態ファクトリは、プリンタを表す属性セットを返すことができます。
指定された引数を使用してデータを返すことができない場合、ファクトリはnullを返すものとします。 たとえば、プリンタ状態ファクトリにディスク・オブジェクトが与えられた場合は、nullを返します。 ほかの状態ファクトリが試されるべきではない場合、その状態ファクトリは例外をスローするだけです。 したがって、ファクトリはその実装からスローされる例外に留意すべきです。 たとえば、プリンタ状態ファクトリにプリンタ・オブジェクトが与えられたが属性が矛盾する場合には、ファクトリは例外をスローします。
最終的に、ファクトリの出力形式は、基本的なネーミング・サービスまたはディレクトリ・サービスによって決まります。 たとえば、CORBA Object Services (COS)ネーミング・サービスのためのコンテキスト実装がサービスに保存できるのはCORBAオブジェクト参照のみであり、LDAPのためのコンテキスト実装が保存できるのは属性のみです。ただし、これらの属性内の情報のエンコードには多くの柔軟性があります。
サービス・プロバイダは通常、予測される入力の各(共通)タイプに対してファクトリを指定し、アプリケーションは、そのセットを独自の状態ファクトリで拡張できます。 たとえば、COSネーミングのサービス・プロバイダに、Java Remote Method Invocation (RMI)オブジェクトをCORBAオブジェクト参照に変換するための状態ファクトリがあるとします。 そのプロバイダのユーザーは、Microsoft COMオブジェクト参照をCORBAオブジェクト参照に変換するための状態ファクトリを追加できます。
JNDIは、java.naming.factory.stateプロパティで指定されている状態ファクトリを探します。このプロパティは、環境内またはプロバイダ・リソース・ファイルにあります(セクション2.9.5を参照)。 このプロパティには、状態ファクトリの完全修飾クラス名のコロンで区切られたリストがあります。 各クラスはStateFactoryインタフェースを実装し、引数をとらないpublicコンストラクタを持たなければなりません。 リスト内のクラスごとに、JNDIはファクトリ・クラスをロードしてインスタンス化し、指定されたオブジェクト、名前、コンテキスト、環境、および属性引数を使用して、そのインスタンスに対してStateFactory/DirStateFactory.getStateToBind()メソッドの呼出しを試みます。 ファクトリから生じる結果がnull以外の場合は、その結果が返されます。それ以外の場合は、JNDIはリスト内の次のクラスに同じ手順を使い、リストが終わるかファクトリがnull以外の結果を返すまでこれを繰り返します。
LDAP v3プロトコルを使用すると、応答コントロールに、サーバーから送信された任意の応答を添付できます。 コントロールは、OID文字列IDとASN.1 BERでエンコードされたバイト・コード列からなります。 外部の情報や補助が存在しない場合、コンテキスト実装は、OIDとバイト列を返すControlインタフェースの簡単な実装を返します。
JNDIは、応答コントロールを処理するための次の抽象クラスを提供します。
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のgetControl-Instance()メソッドを呼び出して、そのコントロールをよりユーザー・フレンドリなアクセス・メソッドを持つコントロールにナロー変換できるコントロール・ファクトリを検索します。 そのようなコントロールは、たとえばASN.1 BERのバイト列を復号化し、情報をJavaタイプとして返せるメソッドへのアクセスを提供できます。 このようなコントロール・ファクトリが見つからない場合は、元の応答コントロールが返されます。 次に、時間を復号化する仮想のTime-ResponseControlの例を示します。
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
}
}
JNDIは、java.naming.factory.controlプロパティで指定されている応答コントロール・ファクトリを探します。このプロパティは、環境内またはプロバイダ・リソース・ファイルにあります(セクション2.9.5を参照)。 このプロパティには、コントロール・ファクトリの完全修飾クラス名のコロンで区切られたリストがあります。 各クラスはControlFactoryインタフェースを実装し、引数をとらないpublicコンストラクタを持たなければなりません。 リスト内のクラスごとに、JNDIはファクトリ・クラスをロードおよびインスタンス化し、指定されたコントロール、コンテキスト、および環境引数を使用して、そのインスタンスに対してControlFactory.getControlInstance()インスタンス・メソッドの呼出しを試みます。 ファクトリから生じる結果がnull以外の場合は、その結果が返されます。それ以外の場合は、JNDIはリスト内の次のクラスに同じ手順を使い、リストが終わるかファクトリがnull以外の結果を返すまでこれを繰り返します。
ファクトリ内のメソッドにパラメータとして渡されるオブジェクトはすべて、呼出し側によって所有されます。 したがってファクトリは、操作またはオブジェクトの修正の期間を超えてオブジェクトへのポインタを保持することを禁じられています。 ファクトリがパラメータに含まれる情報を操作期間を超えて保存する必要がある場合は、パラメータのコピーを独自に保持するものとします。
ファクトリのインスタンスは再入可能とします。 つまり、あるファクトリの単一のインスタンス上のメソッドを複数のスレッドが同時に呼び出すことができるものとします。
CannotProceedExceptionは、コンテキストの内部メソッドのいずれかにより、「処理中の名前がそのネーミング・システムの範囲を越えていること」を検知したときにスローされていることがよくあります。 例外をスローするプロセスは、コンテキストの実装に依存しています。3 これはアプリケーション内のコードであることに注意してください。 「連合内での操作の続行」のコード例は、コンテキスト実装内のコードです。4 この目的に、Hashtableのサブクラス(Propertiesなど)を使用することもできます。5 このドキュメントで「URL」は、RFC 1738とそれに関連するRFCにより定義されるURL文字列を指します。 これは構文規則に準拠する任意の文字列であり、対応するサポートがjava.net.URLクラスまたはWebブラウザに必ずあるとはかぎりません。 URL文字列は、String名前パラメータ、またはNameパラメータの最初のコンポーネントとして渡されます。