[先頭へ] [前へ] [次へ] [最後へ]

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.spi パッケージ

この図の説明は、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 は、名前空間に格納されている情報を使用したオブジェクトの作成をサポートするためのインタフェースです。DirObjectFactory は、DirContext インタフェースを実装するコンテキスト実装で使用するための ObjectFactory のサブインタフェースです。詳細は、セクション 4.1 を参照してください。

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

1.2.4 状態ファクトリ

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

1.2.5 連合のサポート

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

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

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

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

2.1 パラメータの所有権

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

パラメータの所有権の目的から、コンテキストインスタンス上の操作は、その操作によって生成されたいずれかの照会がまだ追跡されている間か、またはその操作によって NamingEnumeration が返された場合はその列挙がまだ使用されている間は、完了したとは見なされません。

2.2 再入可能性

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

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

並行性の制御の目的から、コンテキストインスタンス上の操作は、その操作によって生成されたいずれかの照会がまだ追跡されている間か、またはその操作によって NamingEnumeration が返された場合はその列挙がまだ使用されている間は、完了したとは見なされません。

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

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

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

名前引数を (String または Name として) 受け入れる Context インタフェースまたはサブインタフェース内のメソッドの場合、空の名前は現在のコンテキストを示します。たとえば、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 を解決するときのコンテキストです。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()

BindingSearchResult の場合、コンテキスト実装は、getObjectInstance() またはそれと同等のメソッドの呼び出しの結果であるオブジェクトをコンストラクタに渡すか、またはその getObject() 実装が戻る前に getObjectInstance() またはそれと同等のメソッドを呼び出すように BindingSearchResult のデフォルトの実装をオーバーライドするようにしてください。

次に例を示します。プリンタが 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 を解決するときのコンテキストです。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 {
        ... 
}

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

コンテキスト実装がさまざまな型のオブジェクトを格納できる場合は、次の一般的な型について、次の順序に従うようにしてください。

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

2.5 連合のサポート

2.5.1 名前

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

コンテキストに Name 引数が与えられたとき、それが CompositeName のインスタンスである場合は、合成名として処理されます。それ以外の場合は、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() が複数のネーミングシステムをたどることが必要になる場合があります。これらのネーミングシステムの一部では、DirContext インタフェースはサポートされず、Context インタフェースのみがサポートされています。これらのネーミングシステムは、ターゲットのコンテキストに向けての解釈のための中間的存在として使用されます。ターゲットコンテキストは、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 ネーミングシステムの境界

(マルチコンポーネント) 合成名の解決は、通常は対応するコンテキスト実装によって処理される各ネーミングシステムにまたがるコンポーネントを解決しながら、あるネーミングシステムから次のネーミングシステムへと進められます。コンテキスト実装の観点から見ると、コンテキスト実装は、自身が担当していないコンポーネントを次のネーミングシステム (のコンテキスト実装) に渡します。

次のネーミングシステムのためのコンテキスト実装を見つけるための方法はいくつかのあります。あるネーミングシステム内の名前が次のネーミングシステム内のコンテキスト (またはコンテキストへの 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 の合成名区切り文字が使用されます。これは、次のネーミングシステムの暗黙的ポインタと呼ばれます。

ただし、次のネーミングシステムが見つかったら、コンテキスト実装は、次のネーミングシステムに合成名の解決すべき残りの部分を渡す必要があります。

2.5.5 連合内での操作の続行

複数の名前空間にまたがる名前に対する操作を実行する場合、中間のネーミングシステム内のコンテキストは、その操作を次のネーミングシステムに渡す必要があります。コンテキストは、これを行うためにまず、その操作がどこまで進んだかを正確に示す情報を含む 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 をスローします。

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

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

たとえば、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 を構築します。空白要素は次のネーミングシステムへの暗示的なポインタとして使用され、次のネーミングシステムへの位置まで解釈が進んだことを示します。解釈済みのオブジェクトと解釈済みの名前の値がどのように対応するかに留意してください。コンテキスト実装は次に、CannotProceedExceptiongetContinuationContext() に渡します。

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

2.5.7 CannotProceedException の詳細

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

2.5.8 コンテキスト上の情報

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

連合についてのこれまでの説明をまとめると、次のようになります。複数の名前空間にまたがる名前に対する操作を実行する場合、コンテキスト実装はまず、その操作がどこまで進んだかを正確に示す情報を含む CannotProceed-Exception を構築します。次に、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() は、指定されたオブジェクトのスキーマクラス定義を含むコンテキストを返します。一部のシステムにはグローバルなスキーマが 1 つしかないため、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() 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 を参照してください。

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 クラス (およびサブクラス) は、初期コンテキストファクトリビルダー (下で説明) をインストールすることによってオーバーライドできるデフォルトのアルゴリズムを使用して実装を選択します。

InitialDirContext は、InitialContext の拡張です。初期コンテキストを使ってディレクトリ操作を行うために使用されます。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 の例が含まれています。

コンテキストインスタンスが作成されたあと、URL 以外の名前を使用して InitialContext でメソッドが呼び出されると (下を参照)、そのメソッドがそのコンテキストインスタンスに転送されて呼び出されます。

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

特定の初期コンテキストを使用するアプリケーションは、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 のための 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 操作を実行するために使用されます。

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 サポート) は、どれも使用されません。

初期コンテキストファクトリビルダーの実装は、InitialContext-FactoryBuilder インタフェースを実装する必要があります。その createInitialContextFactory() メソッドによって、InitialContextFactory のインスタンスが生成されます。

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

3.4 InitialContext のサブクラスの実装

ContextDirContext、または LdapContext から拡張されたインタフェースをサポートする初期コンテキストを提供する必要がある場合、サービスプロバイダは、InitialContext (または InitialDirContext/InitialLdapContext) のサブクラスを指定する必要があります。

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 新しいメソッドのサポート

名前引数がないか、または 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);
}

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 は、Reference と環境を引数として使用して、ObjectFactory インスタンスに対して getObjectInstance() を呼び出すことによってオブジェクトを作成します。その結果は、getClassName() で識別されるクラスのインスタンスです。

アプリケーションに返されたオブジェクトをインスタンス化するために必要なすべてのクラスが、JNDI によって提供されるメカニズムを使用して使用可能になることに注意してください。アプリケーションは、ローカルでクラスをインストールする必要はありません。

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

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

プリンタの例に戻り、Printer がプリンタを表すためのインタフェースであり、BSDPrinter クラスがそのインタフェースの実装であるとします。BSDPrinterReferenceable インタフェースを実装し、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 フレームワークの協力を通して透過的に実行されます。

このようなオブジェクトのためのサービスプロバイダは、次のことを行う必要があります。

  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() メソッドを呼び出そうとします。生成が成功した場合は、結果として得られたオブジェクトが返されます。それ以外の場合は、リストが終わるか、またはファクトリが null 以外の結果を返すまで、JNDI はリスト内の次のクラスに対して同じ手順を実行します。

図 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)、そのオブジェクトの名前とバインドされている場所に関するオプション情報、および必要に応じて何らかの追加の環境情報 (名前空間にアクセスしているユーザーに関する何らかの ID または認証情報など) が与えられると、ファクトリはバインディングに適したオブジェクトの生成を試みます。普通、状態ファクトリは目的のネームサービスまたはディレクトリサービスとコンテキスト実装の両方またはどちらかについての知識を持ち、どのデータ形式が許容されるかを認識しています。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 でエンコードされたバイトコード列からなります。外部の情報や補助が存在しない場合、コンテキスト実装は、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
        }
}

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 は、コンテキスの内部メソッドのいずれかが「処理中の名前がそのネーミングシステムの範囲を越えていること」を検知したときにスローされていることがよくあります。例外をスローするプロセスは、コンテキストの実装に依存しています。3 これはアプリケーション内のコードであることに注意してください。「連合内での操作の続行」のコード例は、コンテキスト実装内のコードです。4 この目的に、Hashtable のサブクラス (Properties など) を使用することもできます。5 このドキュメントで「URL」は、RFC 1738 とそれに関連する RFC により定義される URL 文字列を指します。これは RFC の構文規則に準拠する任意の文字列であり、対応するサポートが java.net.URL クラスまたは Web ブラウザに必ずあるとはかぎりません。URL 文字列は、String 名前パラメータまたは Name パラメータの最初のコンポーネントとして渡されます。

Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.