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 コンテキストを指名する) を使ってコンテキストを構築できます。この場合には、偶然 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 Inocation (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 文字列を指します。これは RFC の構文規則に準拠する任意の文字列であり、対応するサポートが java.net.URL
クラスまたは Web ブラウザに必ずあるとはかぎりません。URL 文字列は、String
名前パラメータまたは Name
パラメータの最初のコンポーネントとして渡されます。