3 OUDプラグインAPIリファレンスの使用
OUDプラグインAPIを使用して、リクエストおよびレスポンス、結果および検索リクエストの構成フィルタを処理します。
Oracle Unified Directory (OUD) Javaクラス、メソッドおよび関連する構文と使用方法の詳細は、Oracle Unified Directory Java APIリファレンスを参照してください。
この章では、OUDプラグインAPIの使用に関する一般情報について説明します。
3.1 OUDプラグイン構成の概要
OUDプラグインAPIには、プラグイン構成を格納、取得、変更および検証するための便利な方法が提供されています。
次の項では、OUDプラグインを操作するための概要および例について説明します。
3.1.1 OUDプラグイン構成の格納について
OUDでは、プラグイン構成がプラグイン構成エントリの一部として格納されます。構成要素はOUDのconfig.ldif
ファイルにキーと値のペアとして格納されます。分かりやすくするために、このメカニズムを使用してください。ただし、OUDプラグイン・アーキテクチャを使用すると、外部ファイルなどの代替方法を使用して構成を取得できます。
プラグイン構成は、デフォルトの構成モデルでキーと値のペアのセットとして表されます。キーと値はOUDサーバーおよびdsconfig
コマンド行ツールでRaw文字列として処理されます。キーと値のペアはdsconfig
ツールとプラグイン・ワークフロー要素に関連付けられたplugin-properties
プロパティを使用して設定できます。
詳細は、次の例を参照してください。
3.1.1.1 プラグイン・プロパティを追加する例
プラグイン・プロパティの追加の次の例では、プラグイン・プロパティを追加する方法を示します。
dsconfig set-workflow-element-prop \
--element-name ExamplePlugin \
--add plugin-properties:customProperty=localDB1 \
--hostname host1 \
--port 4444 \
--trustStorePath install-dir/OUD/config/admin-truststore \
--bindDN cn=Directory\ Manager \
--bindPasswordFile ****** \
--no-prompt
3.1.1.2 カスタム・プロパティを構成する例
カスタム・プロパティの構成の次の例では、プラグインExamplePlugin
がcustomProperty
というカスタム・プロパティとともに構成されます。このプロパティは汎用のplugin-properties
パラメータの値として指定されます。
$dsconfig get-workflow-element-prop --element-name ExamplePlugin Property : Value(s) ----------------------:------------------------------------------------------- enabled : true next-workflow-elements : localDB1 plugin-class : oracle.oud.plugin.example.ExamplePlugin plugin-properties : customProperty=localDB1
3.1.2 OUDプラグイン構成の取得
OUDプラグイン構成は、プラグインの初期化中に提供されるPluginConfiguration
インスタンスから使用できます。initializePlugin
メソッドをオーバーライドして、OUDプラグイン構成にアクセスできます。
OUDプラグイン構成にアクセスするためのinitializePluginのオーバーライドの次の例では、initializePlugin
メソッドのオーバーライドを示します。
@Override public void initializePlugin(PluginConfiguration configuration,PluginContext context) throws PluginException { // Plugin configuration as a Set of properties Set<String> properties = configuration.getProperties(); String aParameter=null; for(String value: properties) { if ( value.startsWith("customProperty=") ) { aParameter = value.substring(value.indexOf("=")+1); break; } } // Expected property not found if ( aParameter == null ) { throw new PluginException (context.getTypeBuilder().newMessage("customProperty missing in configuration.")); } // Either use the configuration right now or make it persistent using class members. }
この例で、構成はraw構成オブジェクトからプロパティのセットとして取得されます。読み込まれたプロパティはすぐに使用することも、将来プラグインを実装するJavaクラスのメンバーで使用するために格納することもできます。
3.1.3 プラグイン・プロパティの自動化パーサーの作成
プラグイン構成を取得するための代替方法として、プラグイン・プロパティの自動化パーサーを作成できます。プラグイン・プロパティの自動化パーサーを作成するには、次の手順に従います。
プラグイン・プロパティの自動化パーサーを作成する手順は次のとおりです。
3.1.4 OUDプラグイン構成の動的変更
プラグイン構成に対する変更は、メソッドhandleConfigurationChange()
をオーバーライドすることで動的にキャッチできます。
変更されたプラグイン構成の取得の次の例に示すように、新しい構成を取得できます。
@Override public void handleConfigurationChange(final PluginConfiguration configuration) throws PluginException { // The new configuration is stored in the configuration object // parse again the plugin configuration String aParameter; Set<String> properties = configuration.getProperties(); for(String value: properties) { if ( value.startsWith("customProperty=") ) { aParameter = value.substring(value.indexOf("=")+1); break; } } }
handleConfigurationChange()
メソッドは、OUDサーバーにより管理されているプラグイン・プロパティの更新時にのみ起動されます。構成を外部ファイルに格納するように決定した場合、ファイル・コンテンツに対する変更はここで説明したメカニズムでは動的に検出されません。
3.2 OUDプラグインAPIを使用したリクエスト処理
OUDプラグインAPIを使用して、OUDサーバーのLDAPリクエストを処理でき、検索リクエストを変更し、リクエストを転送し、リクエストを戻します。
この項の内容は次のとおりです。
3.2.1 OUDプラグインAPIを使用したLDAPリクエスト処理の概要
プラグインは、oracle.oud.RequestManager
インタフェースによって定義された対応するコールバックを実装することで、OUDサーバーで処理された任意のLDAPリクエストをインターセプトできます。各タイプのLDAP操作がハンドラ・メソッドに対応しています。たとえばadd
操作はhandleAdd()
メソッドによって管理される、というように順に対応しています。
受け取ったLDAPリクエストはサーバーによって処理されます。このように、リクエストのプロパティを変更することでパフォーマンス、整合性およびセキュリティに関してサーバーに影響を及ぼすことができます。
LDAPリクエストに含まれる各プロパティをゲッターで取得し、セッターで変更できます。
各ハンドラは、関連付けられた3つのパラメータをとります。
-
このプラグインの前のワークフロー要素によって提供されたとおりのすべてのリクエスト・プロパティを含むLDAPリクエスト
-
前のワークフロー要素(一度処理されたLDAPリクエストの結果)に戻るために使用する参照である結果ハンドラ
-
サーバーの各種要素(ロギング・サブシステム、プラグインAPIオブジェクトの作成、クライアント接続、リクエストの破棄など)へのアクセスを提供するツールボックス参照であるコンテキスト
bind
リクエストは4つ目のパラメータをとりますが、これはLDAPプロトコルのバージョンであり便宜上の目的でのみ提供されています。abandon
およびunbind
メソッドはインターセプトできません。リクエストのabandon
はリクエストのコンテキストを使用して検出できます。unbind
操作は、クライアント接続がサーバーから切断されることを意味します。
プロセス・チェーン内の各プラグインに配慮する必要がある規定は、LDAPリクエストを受け取ったときの状態のまま返すことです。これはリクエスト・ハンドラのすべての実装に適用されます。これはプラグインで実行される最も重要な事項です。これが重要な理由は、リクエストにすでに結果があってもリクエストが完了していない場合があるためです。
次の例を検討してください: プラグインはロードバランサ後に実行される処理の一部です。リクエストを変更し、受け取った状態でリクエストを返すかわりに変更されたリクエストを返すと、ロードバランサが正しく機能しない場合があります。実際に、このリクエストは最初のルートで変更され、最初のルートが失敗した場合は2番目のルートで再実行されて変更される可能性があります。
要約すると、リクエストは受け取ったときとまったく同じ形で発行する必要があることに注意してください。
3.2.2 プラグインAPIを使用したOUD検索リクエストの変更
プラグインAPIを使用した検索リクエストの範囲を変更するために、OUD検索リクエストが変更されます。
次の例では、検索リクエストの有効範囲が変更されます。検索の有効範囲はBASE_OBJECT
に変更され、検索リクエストが処理されたときに復元されます。
-
検索リクエストをインターセプトするには、サンプルのプラグインの
handleSearch(...)
メソッドをオーバーライドします。@Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, final SearchResultHandler resultHandler) throws UnsupportedOperationException { System.out.println("plug-in: search received " + request); // Store the received search scope. SearchScope scopeReceived = request.getScope(); // Set a base search scope for all search requests request.setScope(SearchScope.BASE_OBJECT); System.out.println("plug-in: search modified " + request); // Forward the request to the next plug-in. this.getConfiguration() .getFirstNextPlugin() .handleSearch(requestContext, request, resultHandler); // Restore the original value to give the request back as received. request.setScope(scopeReceived); }
-
Oracle Unified Directoryインスタンスを再起動してJARファイルの変更を有効にします。
-
OUDインスタンスを停止します。
-
プラグインJARファイルを
lib
ディレクトリにコピーします。 -
OUDインスタンスを再起動します。
-
-
次のコマンドを実行します。
- UNIX、Linuxの場合
$ ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile /tmp/password --searchScope sub --baseDN "uid=user.1,ou=people,dc=example,dc=com" "(objectclass=*)"
- Windowsの場合
C:\ ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile C:\tmp\password --searchScope sub --baseDN "uid=user.1,ou=people,dc=example,dc=com" "(objectclass=*)"
-
各コマンドごとに、ログ・ファイル (UNIXまたはLinuxではinstance-dir/
OUD/logs/server.out
、Windowsではinstance-dir\OUD\logs\server.out
)に次のような情報を含む必要があります。plug-in: search received SearchRequest(name=uid=user.1,ou=people,dc=example,dc=com, scope=sub, dereferenceAliasesPolicy=never, sizeLimit=0, timeLimit=0, typesOnly=false, filter=(objectClass=*), attributes=[], controls=[]) plug-in: search modified SearchRequest(name=uid=user.1,ou=people,dc=example,dc=com, scope=base, dereferenceAliasesPolicy=never, sizeLimit=0, timeLimit=0, typesOnly=false, filter=(objectClass=*), attributes=[], controls=[])
注意:
リクエストは、呼出しにより次のプラグインに渡されます。
this.getConfiguration().getFirstNextPlugin().handleSearch(...)
これはまさしく
AbstractPlugin
のデフォルトの実装により実行されることです。次を呼び出すことで同じことを実現できます。super.handleSearch(...)
3.2.3 ラッパー・オブジェクトを使用した検索リクエストの変更
リクエストの代替変更方法は、元のリクエストをラッパーという名前の特殊なオブジェクトにラッピングすることです。リクエスト・ラッパーは、ラッピングするリクエストとまったく同じJavaインタフェースを提供する実装で、メソッド上で実行されるすべての呼出しをラッピングされたリクエストに転送します。
プロパティの値を変更するには、該当するメソッドをオーバーライドします。次の例に、検索リクエストの有効範囲を変更する方法を示します。
@Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, final SearchResultHandler resultHandler) throws UnsupportedOperationException { SearchRequest newRequest = new SearchRequestWrapper(request) { @Override public SearchScope getScope() { // Change the scope of this request. return SearchScope.BASE_OBJECT; } }; // Forward the request to the next plug-in. this.getConfiguration() .getFirstNextPlugin() .handleSearch(requestContext, newRequest, resultHandler); }
この代替方法にはラッピングされたリクエストがそのまま残るという利点があります。このように、有効範囲プロパティは変更されなかったため復元する必要はありません。
ただし、この代替方法を使用する場合、問題が発生することがあります。ラッピングされたリクエストは、そのアウトバウンドのラッパーについて認識していません。処理がラッパー・リクエストのレベルで実行される場合で、この処理にラッパーのレベルで再定義されたプロパティが含まれている場合、それらのプロパティは無視されます。ラッピングされたリクエストは、それ自身のプロパティに対してのみアクセス権があります。
ラッパーはpackage oracle.oud.requests
のすべてのタイプのリクエストに対して提供されます。
3.2.4 OUDプラグインAPIを使用したリクエストの転送
多くの場合、プラグインはリクエストをインターセプトし、なんらかの処理を実行してからチェーン内の次のワークフローにリクエストを転送します。非常に多くの場合で、次のワークフロー要素は正確に1つのみ存在します。この場合、リクエストはスーパー・インスタンスの対応するメソッドを起動することで次の要素に渡すことができます。
リーフ・プラグインの場合、oracle.oud.AbstractPlugin
により実装されるすべてのリクエスト・ハンドラをオーバーライドする必要があり、リクエストが次の項で説明されているようにして返される必要があります。
一部の特殊なケースでは、プラグインの後にワークフロー要素がいくつか続く場合があります。プラグインの実装は、どのワークフロー要素にリクエストを転送するかを判断する必要があります。次のワークフロー要素の一覧は、PluginConfiguration
インスタンスからgetNextPlugins()
メソッドまで取得できます。その後、次の例に示されているように、適切なメソッドを直接起動することでリクエストが適切なワークフロー要素に転送されます。
@Override public void handleBind(final RequestContext requestContext, final int version, final BindRequest request, ResultHandler resultHandler) throws UnsupportedOperationException { // Get the original bind DN from the bind request DN originalDn = request.getName(); // Transform the bind DN according to custom algorithm DN newDn = transformDN(originalDn); BindRequestWrapper wrapper = new BindRequestWrapper(request); // Update the wrapper object wrapper.setName(newDn); // Retrieve the list of next plugins and figure out which one to use List<Plugin> nextPlugins = this.getConfiguration().getNextPlugins(); // Pass the request to the appropriate plugin (assume the first one here) nextPlugins.get(0).handleBind(requestContext, version, wrapper, resultHandler); }
3.2.5 OUDプラグインAPIを使用した結果の戻し
場合によっては、プラグインがリクエストをインターセプトし、チェーン内の次のワークフロー要素にリクエストを転送するかわりに自分自身で結果を返すことができます。
結果オブジェクト・インスタンスは、oracle.oud.plugin.PluginContext.TypeBuilder
クラスのnewResult()
メソッドを使用して作成できます。その後、この結果は、ハンドラ・メソッドの引数として渡されたresultHandler
オブジェクトからhandleResult()
またはhandleErrorResult()
メソッドを呼び出すことでプラグインの呼出し元に返すことができます。次の例に、bind
リクエストをインターセプトしてInvalid Credentials
エラーを返す方法を示します。
@Override public void handleBind(final RequestContext requestContext, final int version, final BindRequest request, ResultHandler resultHandler) throws UnsupportedOperationException { // Get the original bind DN from the bind request DN originalDn = request.getName(); // Apply custom logic to decide whether access is granted or not // Assume invalid credentials // Create a Result object Result error = getPluginContext().getTypeBuilder().newResult(ResultCode.INVALID_CREDENTIALS); // Return it to the plugin caller resultHandler.handleErrorResult(error); }
同様に、LDAPエントリをoracle.oud.plugin.PluginContext.TypeBuilder
クラスのnewSearchResultEntry()
メソッドを使用して作成できます。その後、このエントリは、handleSearch()
メソッドの引数として渡されたsearchResultHandler
オブジェクトからhandleSearchResultEntry()
またはhandleErrorResult()
メソッドを起動することでプラグインの呼出し元に返すことができます。
3.3 OUDプラグインのレスポンスの処理
レスポンスをインターセプトする必要があるプラグインは、次のワークフロー要素にリクエストを発行する前に独自のResultHandler
インスタンスを提供することで対象を明示的に登録する必要があります。ResultHandler
のhandleResult()
メソッドは、引数で渡された対応するResult
インスタンスで操作が成功したときに起動されます。逆に、ResultHandler
のhandleErrorResult()
はエラーが発生したときに起動されます。
カスタムのResultHandler
実装で結果を調べて変更できますが、元のErrorHandler
の適切なメソッド(handleResult()
またはhandleErrorResult()
)を起動して呼出し元のワークフロー要素に結果を渡す責任があります。分かりやすくするために、カスタムのResultHandler
をDefaultResultHandler objectclass
の特殊化として実装してください。デフォルトでは、結果とエラーはチェーン内の上流のワークフロー要素に渡され、該当するメソッドのみをプラグイン実装によってオーバーライドする必要があります。
同様に、最終検索結果および検索エントリの両方をインターセプトするには検索操作にSearchResultHandler
を使用する必要があります。handleEntry()
メソッドは、次のワークフロー要素によってLDAPエントリが返されるたびに起動されます。カスタムのSearchResultHandler
実装は、元のSearchResultHandler
のhandleEntry()
メソッドを起動してエントリをチェーンに送信する必要があります。
3.3.1 バインド・エラーをインターセプトする例
OUDプラグインはレスポンスをインターセプトし、バインド・エラーはそのようなタイプのレスポンスです。
次の例で、プラグインはバインド・エラーのみをインターセプトします。
@Override public void handleBind(final RequestContext requestContext, final int version, final BindRequest request, ResultHandler resultHandler) throws UnsupportedOperationException { // Create a new ResultHandler to intercept bind result CustomResultHandler customBindHandler = new CustomResultHandler(resultHandler); // Pass the request to the next plug-in with the custom ResultHandler super.handleBind(requestContext, version, request, customBindHandler); } // implementation of a custom ResultHandler to intercept errors private class CustomResultHandler extends DefaultResultHandler { public CustomResultHandler(ResultHandler resultHandler) { super(resultHandler); } @Override public void handleErrorResult(Result error) { // Invoked when Bind fails // Examine the result and implement some logic // Pass the result up the chain super.handleErrorResult(error); } }
3.3.2 検索エントリと最終検索結果をインターセプトする例
OUDプラグインはレスポンスをインターセプトし、検索エントリと最終検索結果のインターセプトはそのようなタイプのレスポンスです。
次の例では検索エントリと最終検索結果をインターセプトします。
@Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, SearchResultHandler resultHandler) throws UnsupportedOperationException { // Create a new SearchResultHandler to intercept search entries // and result CustomSearchResultHandler customHandler = new CustomSearchResultHandler(resultHandler); // Pass the request to the next plug-in with the custom ResultHandler super.handleSearch(requestContext, request, customHandler); } // implementation of a custom SearchResultHandler to intercept entries and errors private class CustomSearchResultHandler extends DefaultSearchResultHandler { public CustomSearchResultHandler(SearchResultHandler resultHandler) { super(resultHandler); } @Override public void handleErrorResult(Result error) { // Invoked when Search fails // Examine the result and implement some logic // Pass the result up the chain super.handleErrorResult(error); } @Override public void handleResult(Result result) { // Invoked when Search complete // Examine the result and implement some logic // Pass the result up the chain super.handleResult(result); } @Override public boolean handleEntry(SearchResultEntry entry) { // Invoked for every search entry to be returned // Examine the result and implement some logic // Pass the entry up the chain return super.handleEntry(entry); } }
3.4 OUDプラグインの結果処理について
リクエスト結果が結果ハンドラと呼ばれるオブジェクトを使用して返されます。すべてのLDAP操作が、検索操作を除いて同じ種類の結果を共有します。検索操作にはエントリおよび参照である追加の結果が含まれます。LDAP操作は、リクエストと結果ハンドラのペアで構成されます。
リクエストは、リクエストのプロパティにアクセスするために使用します。結果ハンドラは、処理されたリクエストの結果を前のプラグインにポストするために使用します。
この項の内容は次のとおりです。
3.4.1 OUDプラグインの検索結果の無視
プラグイン自体がスキップされ、次のプラグインが戻す結果が次のプラグインから前のプラグインに直接渡される状況では、検索結果を無視する必要があります。
次の例で、前のプラグインにより提供された結果ハンドラは次のプラグインに直接渡されます。その結果、次のプラグインにより返される結果は次のプラグインから前のプラグインに、プラグイン自身をスキップして直接渡されます。リクエストが処理されたことを検出する方法は、handlerSearch(...)
呼出しから戻る方法のみです。
@Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, final SearchResultHandler resultHandler) throws UnsupportedOperationException { // Pass the resultHandler reference received from the previous plug-in to // the next plug-in. This implies that the next plug-in will post the // result of the search request directly to the previous plug-in. this.getConfiguration() .getFirstNextPlugin() .handleSearch(requestContext, request, resultHandler); // The search request was processed by next plug-in. }
3.4.2 OUDプラグインの検索エラーのインターセプト
後続のプラグインにより返される結果をインターセプトするには、プラグインが独自の結果ハンドラを提供する必要があります。
結果ハンドラでは2つのメソッドを定義します。
-
リクエストが成功したときに次のプラグインにより呼び出される
handleResult(Result)
-
リクエストが成功したときに次のプラグインにより呼び出される
handleErrorResult(Result)
検索結果ハンドラでは2つの追加メソッドを定義します。これらのメソッドは、次のプラグインがまだその他のエントリや参照を返せることを指定するTrue
、または予想されるエントリや参照がもうないことを次のプラグインに示すFalse
を返す必要があります。たとえば、サイズの限度に達した場合は予想されるエントリや参照はもうありません。
-
エントリが返されたときに次のプラグインにより返される
handleEntry(SearchResultEntry)
-
参照が返されたときに次のプラグインにより返される
handleReference(DN, SearchResultReference)
OUDプラグインAPIには、oracle.oud.plugin.DefaultResultHandler
という名前のデフォルトの実装が結果ハンドラの実装のために提供されています。このJavaクラスが結果ハンドラ(ほとんどの場合、前のプラグインにより提供された結果ハンドラ)をラッピングし、受け取った結果をラッピングされた結果ハンドラにデフォルトで転送します。結果をキャプチャするために、プラグインが対象となる種類の結果をオーバーライドする必要があります。同様のデフォルト実装が検索結果ハンドラに存在します: oracle.oud.plugin.DefaultSearchResultHandler
。
次の例に、リクエストが不成功の場合に結果のログを記録する方法を示します。
public class EchoErrorResultHandler extends DefaultResultHandler { public EchoErrorResultHandler(ResultHandler resultHandler) { super(resultHandler); } @Override public void handleErrorResult(Result error) { // Echo the result of the request. System.out.println("plug-in: error result " + error); // Let the default behavior forward the result to the wrapped result // handler super.handleErrorResult(error); } }
次の例に、リクエストが不成功の場合に検索操作で結果を印刷する方法を示します。
@Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, final SearchResultHandler resultHandler) throws UnsupportedOperationException { // The result handler passed to the next plug-in will echo the result in // case the request was not successful. this.getConfiguration() .getFirstNextPlugin() .handleSearch(requestContext, request, new EchoErrorResultHandler(resultHandler)); // The search request was processed by next plug-in. }
次のことに注意してください。
-
結果ハンドラはリクエストに関連付けられていません。リクエストに対する参照を結果ハンドラ内に保持することで関連付けを維持するのは開発者の判断によります。
-
受け取ったリクエストの各インスタンスごとに、カスタムの結果ハンドラの新しいインスタンスが必要です。
3.4.2.1 検索リクエストのエラーのロギング
検索リクエストのエラーのログを記録するには:
-
上に示したようにサンプル・プラグインを変更します。
-
Oracle Unified Directoryインスタンスを再起動してJARファイルの変更を有効にします。
-
OUDインスタンスを停止します。
-
プラグインJARファイルを
lib
ディレクトリにコピーします。 -
OUDインスタンスを再起動します。
-
-
次のコマンドを実行して、存在しないユーザーを検索します。
- UNIX、Linuxの場合
ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile /tmp/password --searchScope sub --baseDN "uid=user.unknown,ou=people,dc=example,dc=com" "(objectclass=*)" the command displays SEARCH operation failed Result Code: 32 (No Such Entry) Additional Information: The search base entry 'uid=user.unknown,ou=people,dc=example,dc=com' does not exist Matched DN: ou=people,dc=example,dc=com
- Windowsの場合
ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile C:\tmp\password --searchScope sub --baseDN "uid=user.unknown,ou=people,dc=example,dc=com" "(objectclass=*)" the command displays SEARCH operation failed Result Code: 32 (No Such Entry) Additional Information: The search base entry 'uid=user.unknown,ou=people,dc=example,dc=com' does not exist Matched DN: ou=people,dc=example,dc=com
各コマンドごとに、ログ・ファイル (UNIX、Linuxではinstance-dir/
OUD/logs/server.out
、またはWindowsではinstance-dir\OUD\logs\server.out
)に次のような情報を含む必要があります。plug-in: error result Result(resultCode="No Such Entry", matchedDN="ou=people,dc=example,dc=com", diagnosticMessage="The search base entry 'uid=user.unknown,ou=people,dc=example,dc=com' does not exist", referrals=null, controls=[])
3.4.3 検索リクエストにより返されたエントリのカウント
リクエスト結果が結果ハンドラと呼ばれるオブジェクトを使用して返されます。EntryCounterResultHandler
によってエントリ数をカウントできます。
次の例では、検索リクエストにより返されたエントリをカウントしてそのログを記録します。EntryCounterResultHandler
は、handleEntry(...)
メソッドが呼び出されるたびにカウンタの値を増やします。
public class EntryCounterResultHandler extends DefaultSearchResultHandler { // The number of search result entries returned by this search result // handler. private int entriesCount; public EntryCounterResultHandler(SearchResultHandler resultHandler) { super(resultHandler); } @Override public boolean handleEntry(SearchResultEntry entry) { this.entriesCount++; return super.handleEntry(entry); } public int getEntriesCount() { return this.entriesCount; } }
検索リクエスト・ハンドラは、処理された各検索リクエストごとに返されたエントリをカウントする結果ハンドラを渡すように変更されます。次のプラグインによりリクエストが処理された後、返されたエントリの数がログに記録されます。次に使用例を示します。
@Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, final SearchResultHandler resultHandler) throws UnsupportedOperationException { EntryCounterResultHandler counter = new EntryCounterResultHandler(resultHandler); // The result handler passed to the next plug-in will count the number of // entries returned by the next plug-in. this.getConfiguration() .getFirstNextPlugin() .handleSearch(requestContext, request, counter); // The search request was processed by next plug-in. System.out.println(String.format("plug-in: request %s returned %d entries", request, counter.getEntriesCount())); }
3.4.3.1 検索リクエストで返されたエントリの数のロギング
検索リクエストで返されたエントリの個数をログに記録する手順は次のとおりです。
-
サンプル・プラグインを上の例に示されているように変更します。
-
Oracle Unified Directoryインスタンスを再起動してJARファイルの変更を有効にします。
-
OUDインスタンスを停止します。
-
プラグインJARファイルを
lib
ディレクトリにコピーします。 -
OUDインスタンスを再起動します。
-
-
次のコマンドを実行して、登録されたすべてのユーザーを表示します。
- UNIX、Linuxの場合
ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile /tmp/password --searchScope sub --baseDN "ou=people,dc=example,dc=com" "(objectclass=*)"
- Windowsの場合
ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile C:\tmp\password --searchScope sub --baseDN "ou=people,dc=example,dc=com" "(objectclass=*)"
各コマンドごとに、ログ・ファイル (UNIXまたはLinuxではinstance-dir/OUD/logs/server.out
、Windowsではinstance-dir\OUD\logs\server.out
)に次のような情報を含む必要があります。
plug-in: request SearchRequest(name=ou=people,dc=example,dc=com, scope=sub, dereferenceAliasesPolicy=never, sizeLimit=0, timeLimit=0, typesOnly=false, filter=(objectClass=*), attributes=[], controls=[]) returned 51 entries the number of returned entries corresponds to the 50 users plus the entry ou=people,dc=example,dc=com
3.5 検索リクエストの構成フィルタ
場合によって、アプリケーションで検索リクエストに含まれたフィルタとの相互作用が必要なことがあります。この相互作用はビジター・デザイン・パターンに基づいたメカニズムにより指定され、Javaインタフェースoracle.oud.types.FilterVisitor<R,P>
により定義されています。
この項の内容は次のとおりです。
3.5.1 検索リクエストのフィルタ処理について
LDAPプロトコルではフィルタのタイプが10個指定されています: and
、or
、not
、equalityMatch
、substrings
、greaterOrEqual
、lessOrEqual
、present
、approxMatch
およびextensibleMatch
。
ビジター・フィルタでは、フィルタの各タイプごとにvisitAndFilter(...)
、visitOrFilter(...)
などのハンドラを定義します。
フィルタが解析される際、解析対象のフィルタを構成するフィルタのタイプが識別されます。識別したタイプに関連付けられたビジター・メソッドが順番に呼び出されます。
FilterVisitor<R,P>
は2つのパラメータをとります:
-
<R>
は各ビジター・ハンドラで返されるタイプです。 -
<P>
は各ビジター・ハンドラに対して提供できるパラメータです。
3.5.2 FilterVisitor
を実装する例
FilterVisitor
は、解析対象のフィルタにおける属性の存在をチェックします。
次の例では、FilterVisitor
の実装を提供します。objectclass=*.
のように属性が値*
に関連付けられている場合、属性がフィルタに存在します。その場合、<R>
が評価の結果に対応します。<R>
は、属性が解析対象のフィルタに存在する場合はTRUE
、属性がない場合はFALSE
の値を持つブール値として定義されています。<P>
はパラメータで、どの属性をチェックする必要があるかを定義する文字列に対応します。解析対象のフィルタがobjectclass=*
の場合、パラメータobjectclass
を使用してビジターを呼び出すとTRUE
が返されます。他の値はFALSE
を返します。
サブフィルタ(and
、or
およびnot
)で構成されたビジターは、それを構成しているすべてのサブフィルタにアクセスしてチェックを転送します。
次の例では、説明のために、関連するビジターも情報を記録しています。
private class PresenceOfFilterVisitor implements FilterVisitor<Boolean, String> { @Override public Boolean visitAndFilter(final String presenceName, final List<Filter> subFilters) { System.out.println("plug-in: visit AND with " + subFilters); boolean result = false; // Iterate through all sub filters with this filter visitor. for(Filter subFilter: subFilters) { result = subFilter.accept(this, presenceName); if ( result ) { break; } } return result ? Boolean.TRUE : Boolean.FALSE; } @Override public Boolean visitApproxMatchFilter(final String presenceName, final String attributeDescription, final ByteString assertionValue) { return Boolean.FALSE; } @Override public Boolean visitEqualityMatchFilter(final String presenceName, final String attributeDescription, final ByteString assertionValue) { System.out.println("plug-in: visit EQUAL with " + attributeDescription + "=" + assertionValue); return Boolean.FALSE; } @Override public Boolean visitExtensibleMatchFilter(final String presenceName, final String matchingRule, final String attributeDescription, final ByteString assertionValue, final boolean dnAttributes) { return Boolean.FALSE; } @Override public Boolean visitGreaterOrEqualFilter(final String presenceName, final String attributeDescription, final ByteString assertionValue) { return Boolean.FALSE; } @Override public Boolean visitLessOrEqualFilter(final String presenceName, final String attributeDescription, final ByteString assertionValue) { return Boolean.FALSE; } @Override public Boolean visitNotFilter(final String presenceName, final Filter subFilter) { System.out.println("plug-in: visit NOT with " + subFilter); // Visit the associated filter with this filter visitor. return subFilter.accept(this, presenceName); } @Override public Boolean visitOrFilter(final String presenceName, final List<Filter> subFilters) { System.out.println("plug-in: visit OR with " + subFilters); boolean result = false; // Iterate through all sub filters with this filter visitor. for(Filter subFilter: subFilters) { result = subFilter.accept(this, presenceName); if ( result ) { break; } } return result ? Boolean.TRUE : Boolean.FALSE; } @Override public Boolean visitPresentFilter(final String presenceName, final String attributeDescription) { System.out.println("plug-in: visit Presence with '" + attributeDescription + "'"); return presenceName.equalsIgnoreCase(attributeDescription) ? Boolean.TRUE : Boolean.FALSE; } @Override public Boolean visitSubstringsFilter(final String presenceName, final String attributeDescription, final ByteString initialSubstring, final List<ByteString> anySubstrings, final ByteString finalSubstring) { return Boolean.FALSE; } @Override public Boolean visitUnrecognizedFilter(final String presenceName, final byte filterTag, final ByteString filterBytes) { return Boolean.FALSE; } }
3.5.3 検索リクエストのobjectclass=*
の存在の検証およびロギングの例
プラグインにより処理される検索リクエスト・フィルタにおけるobjectclass=*
を検証してログを記録することができます。
次の例では、検索リクエスト・フィルタのobjectclass=*
の存在を検証してログを記録します。
@Override public void handleSearch(final RequestContext requestContext, final SearchRequest request, final SearchResultHandler resultHandler) throws UnsupportedOperationException { Filter filter = request.getFilter(); System.out.println("plug-in: visitor returned " + filter.accept(new PresenceOfFilterVisitor(), "objectclass")); // Pass the resultHandler reference received from the previous plug-in to // the next plug-in. This implies that the next plug-in will post the // result of the search request directly to the previous plug-in. super.handleSearch(requestContext, request, resultHandler); // The search request was processed by next plug-in. }
3.5.4 検索リクエストのobjectclass=*
の存在の検証およびロギング
プラグインにより処理される検索リクエスト・フィルタにおけるobjectclass=*
の存在を検証してログを記録できます。
-
「検索リクエストのobjectclass=*の存在の検証およびロギングの例」に示されているように、サンプル・プラグインを変更します。
-
Oracle Unified Directoryインスタンスを再起動してJARファイルの変更を有効にします。
-
OUDインスタンスを停止します。
-
プラグインJARファイルを
lib
ディレクトリにコピーします。 -
OUDインスタンスを再起動します。
-
-
次のコマンドを実行して、登録されたすべてのユーザーを表示します。
- UNIX、Linuxの場合
ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile /tmp/password --searchScope sub --baseDN "ou=people,dc=example,dc=com" "(objectclass=*)"
- Windowsの場合
ldapsearch --hostname localhost --port 1389 --bindDN "cn=directory manager" --bindPasswordFile C:\tmp\password --searchScope sub --baseDN "ou=people,dc=example,dc=com" "(objectclass=*)"
各コマンドごとに、ログ・ファイル (UNIXまたはLinuxではinstance-dir/
OUD/logs/server.out
、またはWindowsではinstance-dir\OUD\logs\server.out
)に次のような情報を含む必要があります。plug-in: visit Presence with 'objectClass' plug-in: visitor returned true
異なるフィルタでコマンドを実行すると、ビジター・メカニズムがどのように機能するかが示されます。フィルタ&(|(!(uid=user.1)))
で検索すると次のログが記録されます。
plugin: visit AND with [(|(!(uid=user.1)))] plugin: visit OR with [(!(uid=user.1))] plugin: visit NOT with (uid=user.1) plugin: visit EQUAL with uid=user.1 plugin: visitor returned false
3.6 OUDプラグインAPIの内部操作の構成
APIはLDAPに似た呼出しをOUD内に作成するメソッドを提供します。最初のメソッドで、プラグインは現在のプラグイン・ワークフロー内でコールし、2番目のメソッドでプラグインはOUDにLDAPのようなコールを行います。
OUDプラグインの内部操作の構成は次の項で説明します。
3.6.1 内部LDAPリクエストについて
内部LDAPリクエストは、クライアントからの外部リクエストによって直接開始されるのではなく、プラグインによって内部的に開始されるという意味で内部的です。内部リクエスト・コールは、プラグインで、クライアント・リクエストが存在しない操作をOUDで実行する必要がある場合に使用します。たとえば、プラグインでユーザー・エントリに対する検索リクエストを実行し、クライアントからバインド・リクエストを受け取ったときに追加の資格証明を取得できます
oracle.oud.plugin.RequestManager
コールバックは、内部操作を含め、OUDにより処理されたすべての操作に対して起動されます。多くの場合、プラグインはクライアント・アプリケーションによって直接開始された操作に対してのみ適用されます。内部操作と通常の操作は、リクエスト・オブジェクトでisInternal()
メソッドを呼び出すことで区別できます。
内部LDAPリクエストは、oracle.oud.plugin RequestBuilder
オブジェクト・クラスを介して作成されます。requestBuilder
に対する参照は、クライアント・アプリケーションから受け取ったリクエストと関連付けられたRequestContext
からgetRequestBuilder()
を介して取得できます。
内部操作を実行するために使用されるユーザー資格証明は、作成時に指定されています。一般に、内部操作は現在のセキュリティ・コンテキスト内で、プラグインをトリガーしたユーザーの資格証明を使用して実行されます。場合によっては、内部操作に特権アクセスが必要なことがあります。たとえば、バインド・リクエストを処理する前に実行される内部検索は、その時点で現在のユーザーがまだ認証されていないためanonymous
として実行されます。
3.6.2 OUDプラグインAPIの内部リクエストの理解
OUDプラグインAPIには、内部リクエストを起動するための2つの方法が提供されています。最初のモードでは、プラグインは該当する後続ワークフロー要素がチェーン内に構成されていれば、それを起動することで現在のプラグイン・ワークフロー内で呼出しを実行します。2つ目のモードでは、プラグインはLDAPに似た呼出しをエンド・クライアントからのものと同様にOUD内に作成します。
各呼出しには、ルーターが操作に適したワークフローを選択できるようにする機能が提供されます。
内部リクエストからの結果は、結果ハンドラを使用して、「OUDプラグインの結果処理について」に説明されているようにして取得できます。
3.6.2.1 OUDプラグインAPIのモード1について
プラグインの次のワークフロー要素は、プラグイン構成から呼出しconfiguration.getNextPlugins()
を介して取得できます。これらのプラグインの名前は、getName()
メソッドを使用して取得できます。名前を取得した後、次のワークフロー要素が複数構成されている場合にはどのワークフロー要素にリクエストを送る必要があるかを選択できます。たとえば、ロード・バランシング・サービスを提供しているプラグインには複数の後続ワークフロー要素が構成されている可能性があります。内部リクエストを開始した後、ターゲット・ワークフローの場所が決まれば、適切なハンドラ・メソッドを介してリクエストを発行できます。
3.6.2.2 OUDプラグインAPIのモード1の実装
次の例では、属性customTimeStamp
を受け入れるためにサーバー・スキーマを変更する必要があります。このプラグインは、内部のmodify
操作を使用してログイン時刻をユーザー・エントリ属性customTimeStamp
に格納します。
処理の時点でバインドはまだ完了していないため、特権リクエスト・ビルダーgetRequestBuilder(true)
を使用する必要があることに注意してください。したがって、ユーザーは匿名とみなされます。
@Override public void handleBind(final RequestContext requestContext, final int version, final BindRequest request, ResultHandler resultHandler) throws UnsupportedOperationException { ... // Get a privileged request builder RequestBuilder myRequestBuilder = requestContext.getRequestBuilder(true); // Create a new modify request using that builder // Target LDAP entry is the user about to be authenticated ModifyRequest addTimestampModifyRequest = myRequestBuilder.newModifyRequest(request.getName()); // Populate the modification object addTimestampModifyRequest.addModification(ModificationType.REPLACE, "customTimeStamp", System.currentTimeMillis()) ; // Create a ResultHandler to catch the result of the modify operation ResultHandler modifyResultHandler = new CustomModifyResultHandler(resultHandler); // submit the request to the next workflow element getConfiguration().getFirstNextPlugins().handleModify(requestContext, addTimestampModifyRequest, modifyResultHandler); ... }
3.6.2.3 OUDプラグインAPIのモード2について
このモードでは、リクエストは内部リクエスト・マネージャ・オブジェクトを介して実行されます。このオブジェクトはRequestContext
からメソッドgetInternalRequestManager()
を介して取得できます。その後、リクエストを適切なハンドラ・メソッドを介して発行できます。
各リクエストは適切なワークフローにルーティングされるため、所定のワークフロー内のプラグインにより開始された内部リクエストが同じワークフローにルーティングされる可能性があります。プラグインが自分自身で生成したリクエストをインターセプトする場合があります。内部操作の処理で予期しない再帰的ループを防ぐために、内部操作が発行されたときに追加の添付(コンテキスト情報)を内部操作に添付できます。この添付は、新しいリクエストを受け取ったときにプロキシにより取得およびチェックしてループを検出し適切なアクションをとることができます。添付は、リクエスト・オブジェクトにより実装されたAttachmentHolder
インタフェースにより管理できます。
3.6.2.4 OUDプラグインAPIのモード2の実装
次の例では、変更の前にユーザーのエントリ内でcustomTimeStamp
属性を検索しています。変更リクエストが現在のユーザー資格証明付きで作成され、内部リクエスト・マネージャを介してエンド・クライアントからのものと同様に発行されます。分かりやすくするために例外処理をコード例から削除しました。
public void handleModify(final RequestContext requestContext, final ModifyRequest request, ResultHandler resultHandler) throws UnsupportedOperationException { ... // Get a standard request builder RequestBuilder myRequestBuilder = requestContext.getRequestBuilder(false); // Create a new search request using that builder // Target LDAP entry is the user about to be modified SearchRequest getLastTimestampRequest = myRequestBuilder.newSearchRequest( request.getName(), SearchScope.BASE_OBJECT, getPluginContext().getTypeBuilder().newFilter("(objectclass=*)"), "currentTimeStamp"); // Create a ResultHandler to catch the result of the search operation SearchResultHandler searchResultHandler = new CustomSearchResultHandler(resultHandler); // submit the request via the internal request manager requestContext.getInternalRequestManager().handleSearch(requestContext, getLastTimestampRequest, searchResultHandler); ... }
次の例では、ループを処理する方法を示します。検索リクエストが初めてプラグインにより受け取られたときは、名前がnbLoops
の添付はありません。プラグインは添付(name=nbLoops
, value=1
)を含むリクエストにフラグを設定し、その後そのリクエストを内部リクエスト・マネージャに対してリバランスします。検索リクエストは最終的にプラグインに戻ります。プラグインが2度目に添付を取得したとき、値を2
に増やし、リクエストに取得したものを設定します。その後、内部リクエスト・マネージャに対してリバランスします。3度目、値(2
)はMAX_LOOPS
より大きいか等しいため、プラグインはリクエストを次のワークフロー要素に送ります(method super.handleSearch(...)
を使用
// Let search requests loop 2 times within the internal request manager, // before sending them to next WorkflowElement public static final int MAX_LOOPS = 2; @Override public void handleSearch(RequestContext requestContext, SearchRequest request, SearchResultHandler resultHandler) throws UnsupportedOperationException { String name = "nbLoops"; Integer nbLoops = 0; Set<String> attachmentNames = request.getAttachmentNames(); // Get "nbLoops" attachment value, if ound in the request if (attachmentNames.contains(name)) { nbLoops = (Integer) request.getAttachment(name); } // if we reach max number of loops... if (nbLoops >= MAX_LOOPS) { // ...remove attachment request.removeAttachment(name); // forward request to next WorkflowElement super.handleSearch(requestContext, request, resultHandler); } else { // increment nbLoops value nbLoops++; // set attachment nbLoops new value request.setAttachment(name, nbLoops); // log request (as internal op) + attachment value Logger logger = requestContext.getLogger(); HashMap<String, String> map = new HashMap<String, String>(); map.put("nbLoops", Integer.toString(nbLoops)); logger.logSearchRequestIntermediateMessage(request, map); // re-balance tge search request via the internal request manager requestContext.getInternalRequestManager().handleSearch(requestContext, request, resultHandler); }
3.7 OUDプラグインの例外について
プラグイン実装は、予期しないエラー状態が発生したときにPluginException
のサブクラスを発生させることができます。
サーバーの動作は例外がいつ発生したかによります。LDAP操作の処理中に発生した場合、クライアント・アプリケーションに対してLDAPエラー80「内部エラー」が返されます。プラグインの初期化中に発生した場合、プラグインは無効になります。
3.8 OUDプラグインAPIのロギングおよびデバッグの例外
OUDプラグインAPIのロギングおよびデバッグの例外を処理できます。oracle.oud.plugin.RequestContext.Logger
インタフェースを使用して、OUDのメッセージのログを記録します。
この項の内容は次のとおりです。
3.8.1 OUDプラグインAPIのロギングおよびデバッグの例外について
プラグインAPI内で生成されたキャッチされない例外は、OUDデバッグ・ログにWarning
レベルで記録されます。
プラグインの標準出力は、そのプラグインをホストしているOUDディレクトリ・サーバー・インスタンスにあるログ・ファイル(UNIXまたはLinuxではinstance-dir/OUD/logs/debug
、Windowsではinstance-dir\OUD\logs\debug
)にリダイレクトされます。
プラグインの開発中に、次のdsconfig
コマンドを使用してデバッグ・ログを有効にできます。
dsconfig set-log-publisher-prop \ --publisher-name "File-Based Debug Logger" \ --set default-debug-level:warning \ --set enabled:true
プラグイン実装は、OUD access
、error
またはdebug
ログにoracle.oud.plugin.RequestContext.Logger
インタフェースを使用してメッセージを記録できます。
3.8.2 クライアント・リクエストの処理中のプラグインのデバッグ
プラグインの実装中に予期しないエラー条件が発生します。IDEを通じてクライアント・リクエストを処理中に、プラグインをデバッグする必要があります。