開発者:Java
   DOWNLOAD
 Oracle SOA Suite (includes OWSM and JDeveloper)
 Sample Code

   TAGS
soa, java, All

Decoratorパターンを使用したWebサービス開発の簡素化

コストのかかるコード/テスト/デプロイといったサイクルを実行せずにWebサービスに機能を適用する方法を学習しま す。

Jason Jones著 Oracle ACE Director

2007年12月公開

Oracle Fusion Middlewareは、Webサービス(Java、BPEL、ESB、PHPなど)を構築する多数のオプションを提供します。 データベースもデータベース・ネイティブWebサービスに参加しています。 また、ほとんどの組織に異機種のITインフラストラクチャがあります。 したがって、サービス指向アーキテクチャ(SOA)は、さまざまなプラットフォーム、ツール、プログラミング言語、およびベンダーからのサービスで構成さ れます。 このため、一般的な動作およびポリシーを組織全体のWebサービスに適用することが非常に困難になります。

基礎となるシステムを変更せずにすべてのWebサービスの動作を変更および拡張できる方法が必要になります。 この問題は、 Gang of Four(GoF)Decoratorパターンと密接に関連しています。 この記事では、 Oracle SOA Suiteコンポーネントの Oracle Web Services Manager(Oracle WSM)を使用してWebサービスを"装飾"する方法を学習します。

Decoratorパターン

GoF Decoratorパターンは、元のオブジェクトを変更せずに、オブジェクトの動作を変更できるオブジェクト指向開発で使用される設計パターンです。 同じインタフェースを実装し、動作を追加して入出力を変更するDecoratorに元のオブジェクトをラップして、これを実行します。 一般的にDecoratorは基礎となる標準オブジェクトに委譲しますが、特定の条件が満たされていない場合に操作を省略する場合があります。

Decoratorパターンの概要を簡単に説明します。 『 Design Patterns: Elements of Reusable Object-Oriented Software』(Addison- Wesley)で詳細を確認することを強く推奨します。

Oracle Web Services Manager

Decoratorは、Oracle WSMの動作を確認する良い方法です。 Oracle WSMで、ポリシーを一元的に定義します。 通常、これらのポリシーは、4つのパイプライン(リクエスト前、リクエスト、レスポンス、レスポンス後)で構成されます。 各ポリシー・パイプラインは、ポリシー・ステップで構成されます。 Oracle WSMコンソールを使用して、このすべてを開発できます。 パイプラインは、メッセージの記録同様に簡単です。 実際、デフォルトの標準ポリシーは、メッセージを記録するリクエストおよびレスポンスのパイプラインで構成されます。

これらのポリシーは、基礎となるWebサービスのDecoratorとして機能します。 また、クライアントの観点からサービスの動作を拡張または変更します。ただし、基礎となるサービスを変更せずにこの目標を達成します。

Oracle WSMを使用して、サービスへのポリシーの柔軟なマッピングを実行できます。 Java EEサーブレット・フィルタのように、Oracle WSMのポリシーをサービスごとに手動でマップするか、URLパターンに割り当てることができます。 また、パイプライン・テンプレートを作成して、サービスまたはURLごとに設定を繰り返さずに、一連のポリシーを簡単に適用できます。

Oracle WSMのアーキテクチャ

Oracle WSMポリシーおよびサービス登録は、データベースによってバックアップされるOracle WSM Policy Managerに一元的に格納されます。 Webベースのユーザー・インタフェースを通じて、ポリシーとサービスが構築されます。 Oracle WSMのポリシーが構築されると中央に格納されますが、多くの実施ポイントにデプロイできます。 実施ポイントでは、Oracle WSM Policy Managerと通信して、現在のポリシーおよびサービス登録を取得します。 実施ポイントには、2つの形式(ゲートウェイとエージェント)があります。

Oracle WSMゲートウェイは、Oracle Application ServerなどのJava EEサーバーで実行されるHTTPプロキシ・サーバーです。 ゲートウェイは、Webサービス・リクエストを受け入れ、ポリシーを適用して(メッセージの操作を含む場合があります)、リクエストの転送を実行するプロ キシとして機能します。 Oracle WSMゲートウェイでは、必要に応じてこれらの同じステップを同期レスポンスに適用できます。 ゲートウェイは、ポリシーを実施するもっとも柔軟なオプションを提供します。 Webサービス・クライアントやサービスのコードまたは構成を変更する必要はありません。クライアントが呼び出すURLだけを変更する必要があります。 たとえば、http://jj620.ciscoinc.com:7777/orabpel/default/SampleService/1.0を呼び 出す代わりに、http://jj620.ciscoinc.com:7777/gateway/services/SampleServiceを呼び出 します。

Oracle WSMエージェントも同じ機能を実行しますが、サービスまたはクライアント・アプリケーションに直接インストールされます。 インプロセスのエージェントでは、Webサービス・クライアントまたはサービス、あるいはその両方の構成変更をおこなう必要があります。 Oracle Application Server Webサービス・スタックを使用している場合、これは非常に簡単です。 たとえば、Oracle Webサービス・クライアントを構成する場合、次のコードを-client-webservices.xml構成ファイルに追加します。

<runtime enabled="owsm">
                                  
<owsm init-home="C:\java\oc4j_101330\owsm\config\interceptors\C0003005" </runtime>

Oracle WSMユーザーが認識する必要がある最後のコンポーネントは、Oracle WSM Monitorです。 このコンポーネントは、実施ポイントから情報を収集します。 パフォーマンス、エラー、統計などのログおよびレポートを作成するためにこの情報が集計されます。

簡単なパイプライン

例として、Oracle Internet DirectoryなどのLDAPディレクトリの内容に基づいてユーザーを認可するパイプラインを構築します。 特定の権限をもつエンドユーザーからのみ呼び出すことができるWebサービスを仮定します。 これを実行する一般的な方法は、アクセス・レベル(読取り専用、編集、管理、管理者など)を表すLDAPグループを保持することです。 アクセスが付与される前にこれらのグループのユーザーが確認されます。

クライアントがWS-Security SOAPヘッダーの資格証明情報を送信して、この方法を実装できます。 以下はその例です。

<env:Envelope>
                                  
<env:Header>
<wsse:Security>
<wsse:UsernameToken>
<wsse:Username>jjones</wsse:Username>
<wsse:Password>test</wsse:Password>
</wsse:UsernameToken>
</wsse:Security> </env:Header> <env:Body> ... </env:Body> </env:Envelope>

Oracle WSMでパイプラインを構築して、これらの資格証明を処理したり、LDAPディレクトリの資格証明を確認したりできます。 最初に、以前の項で登録したサービスにポリシーを割り当てる必要があります。 これを実行するには、ナビゲーション・バーの「 Manage Policies」リンクをクリックして、作成した実施ポイントの横の「 Policies」リンク を選択します。

次に、登録したサービスの編集アイコンの横の「 edit」をクリックします。 デフォルトのパイプラインがすでに移入されています。

次に、"Extract Credentials"ステップを追加します。 このステップは、SOAPメッセージから資格証明情報を抽出します。 リクエスト・パイプラインのログ・ステップの「 Add Step Below」をクリックします。新しいステップのダイアログで、「 Extract Credentials」 を選択して、「 Ok」をクリックします。

HTTP Basic資格証明を確認するため、資格証明の抽出ステップがデフォルトで構成されます。 これを変更してHTTPをWS-BASICに置き換えて、WS-Security UsernameTokenヘッダーを参照できます。 ユーザー資格証明を抽出すると、認可ステップを追加できます。 作成したExtract Credentialsステップの「 Add Step Below」リンクをクリックし、「 Ldap Authorize」ステップを選択し て、「 Ok」をクリックします。 この時点で、リクエスト・パイプラインは次のようになります。

WS-Security UsernameTokenヘッダーを参照するため、資格証明の抽出ステップがデフォルトで構成されます。 ユーザー資格証明を抽出すると、認可ステップを追加できます。 作成したExtract Credentialsステップの「 Add Step Below」リンクをクリックします。 「 Ldap Authorize」ステップを選 択して、「 Ok」をクリックします。 この時点で、リクエスト・パイプラインは次のようになります。

最後に、Authorizeステップを構成する必要があります。 認可ステップの「 Configure」 リンクをクリックし、プロパティを編集して、ローカルのLDAPサーバーを指定します。 "dc=ciscoinc,dc=com"などのユーザーおよびグループのコンテナにLDAP baseDNを設定する必要があります。 ServiceRoles設定は、サービスを呼び出すことができるLDAPグループのカンマ区切りリストです。 最後に、LDAPAdminDNを空にして、LDAPAdminLoginEnabledがfalseに設定されていることを確認しま す。

Ok」をクリックして、「 Next」→「 Save」 をクリックします。 最後に「 Commit」をクリックして、これらのポリシーを保存します。 ユーザーの資格証明に基づいてWebサービスを保護することによって、Webサービスが修飾されます。

カスタム・ステップの書込み

Oracle WSMは、Webサービスを保護および監視できる多くの標準ステップを提供します。 また、アプリケーションのアーキテクチャの強力な機能にもなります。 Java EE開発者は、Javaサーブレット・フィルタに精通しています。 これらのフィルタを使用して、Javaサーブレット、JSP、および任意のWebアプリケーション・フレームワークを修飾することもできます。 Oracle WSMカスタム・ステップでは、SOAPヘッダーと本文を利用できます。 ペイロードは操作可能です。つまり、XML要素を追加、変更、または削除できます。

この項では、GUID(Globally Unique Identifier)を含むカスタムSOAPヘッダーを追加するカスタム・ステップを構築します。 GUIDは、長いランダムな数値または文字列の識別子です。 重複が発生する可能性はありますが、使用できる値が非常に多いので、その可能性は少なくなります。 また、多くのGUIDは、同じ期間に作成されたメッセージを示すMACアドレスまたはIPアドレス、あるいはその両方から派生します。 追加するヘッダーの例を以下に示します。

<env:Envelope>
                                  
<env:Header>
...
<otn:GUID>
B72C0BF6-F795-5A04-4D2F-AA24F2DD9888
</otn:GUID>
... </env:Header> <env:Body> ... </env:Body> </env:Envelope>

グローバルに一意のメッセージIDを使用すると、非常に便利です。 もっとも一般的な使用例は、トラブルシューティングです。複数のコンポーネントを介してそれぞれが個別のログに保存されることが多い単一のWebサービ ス・メッセージで一般的となっています。 メッセージIDを記録し、grepなどのコマンドライン・ツールを使用して、トラブルシューティングでメッセージIDを簡単に検索できます。 Oracle WSMが内部GUIDをすでに生成しているので、下流サービスで使用できるように内部GUIDを利用してSOAPヘッダーとして追加します。

最初に、Oracle JDeveloperの空の新しいプロジェクトを開始します。 次に、プロジェクト・プロパティへ移動して、「 Libraries」 オプションをクリックします。 次のJARをクラスパスに追加します。

$SOA_HOME/owsm/lib/coresv-4.0.jar
                                  
$SOA_HOME/owsm/lib/extlib/axis.jar
$SOA_HOME/owsm/lib/extlib/saaj.jar

完了すると、次のように表示されます。

com.cfluent.policysteps.sdk.AbstractStepを拡張する必要がある新しいJavaクラスを作成 します。 Oracle JDeveloperは、必要なメソッドを実装する必要があることを示すエラーをこの時点で通知します。 「 Quick Fix」バルーンをクリックして、「 Implement Methods...」を選択します。 これによって、IResult execute(IMessageContext context)メソッドのスタブが提供されます。 カスタム・ステップがパイプラインの場合、実行メソッドがOracle WSMエンジンによって実行されます。 Oracle WSMに続行するかどうかを通知するため、IResultのインスタンスを返す必要があります。 通常、実行メソッドは、パイプラインの処理の続行をOracle WSMに通知するIResult.SUCCEEDEDを返します。

メッセージと関連するメタデータがIMessageContextオブジェクトのカスタム・ステップに渡されます。 一般的に、context.getRequestMessage()またはcontext.getResponseMessage()を呼び出して、 SOAPメッセージを取得します。 この場合、メッセージを操作する必要があるため、以下に示すようにIMessageContextインスタンスを com.cfluent.pipelineengine.container.MessageContextに変換する必要があります。

&
                                  
...
String stage = context.getProcessingStage(); // processing stage indicates if this is a request or a response
MessageContext msgContext = (MessageContext)context;
SOAPMessage message = null;
if (IMessageContext.STAGE_PREREQUEST.equals(stage) || IMessageContext.STAGE_REQUEST.equals(stage)) {
message = msgContext.getRequest(); // getting the Axis message
} else if (IMessageContext.STAGE_RESPONSE.equals(stage) || IMessageContext.STAGE_POSTRESPONSE.equals(stage)) {
message = msgContext.getRequest(); // getting the Axis message
}
...

次に、SOAPヘッダーをメッセージに追加します。 最初に、SOAPEnvelopeのハンドルを取得する必要があります。 (これは"Axis" SOAPエンベロープなので注意してください。)

以下のコードについていくつかの注意事項があります。 最初に、SOAPEnvelopeおよびほかの要素をAxisバージョンに変換する必要があります。 Axisバージョンだけがペイロードを変更できます。 次に、ペイロードを変更した場合、setDirty(true)を呼び出して、ペイロードが変更されたことをOracle WSMエンジンに通知する必要があります。 私の経験では、このメソッドが子要素をダーティとして再帰的にマークすることはありませんでした。 これを確認するドキュメントは見つかっていませんが、4レベルの深さのデータを変更する場合にその上位の各要素でsetDirty(true)を呼び出す 必要があります。

...
                                  
// now get the GUID and build a SOAPElement
try {
SOAPEnvelope env = message.getAxisMessage().getSOAPEnvelope();
// next build header element with a name of GUID
SOAPHeaderElement element =
(SOAPHeaderElement)env.getHeader().addChildElement("GUID", "otn", "http://otn.oracle.com/owsmarticle");
element.addTextNode(context.getGUID()); // add the guid that is generated by OWSM

env.setDirty(true); // need to let OWSM know that we've changed the payload.
} catch (Exception e) {
logger.log(Level.SEVERE, "exception occurred while adding GUID header", e);
}
...

最後に、成功メッセージを記録して、SUCCEEDED IResultを返します。 特殊なOracle WSMライブラリを通じて、ロギングを実行する必要があります。 作成するステップを多くのマシンで実行できるので、独自のログ・ファイルまたはSystem.out/errへの書込みが適切ではない場合があります。 Oracle WSMは、Log4JまたはJavaの組込みロギング・クラスに動作が非常に似ているILoggerクラスを提供します。 最後にスーパークラスのAbstractStep.createMethod(...)を呼び出して、Oracle WSMに戻す結果を生成できます。

...
                                  
logger.log(Level.INFO, "successfully added GUID {" + context.getGUID() + "} to SOAP message.");
return createResult(IResult.SUCCEEDED);
...

このステップの完全なソースは、 サ ンプル・ダウンロードzipファイルにあります。

カスタム・ステップのパッケージ化

このカスタム・ステップをデプロイするには、2つの成果物(上記で準備されたクラスのjarファイルおよびOracle WSMのカスタム・ステップ・テンプレート)を用意する必要があります。 ステップ・テンプレートは、使用するクラス、管理者に表示する情報、およびステップの構成に使用できるプロパティ(もっとも重要)を示すXMLディスクリ プタです。 例の一部を以下に示します。

<csw:StepTemplate
                                  
..
name="GUIDStep"
...
>
<csw:Description>Custom step that adds GUID SOAP header</csw:Description>
<csw:Implementation>oracle.otn.guidstep.GUIDStep</csw:Implementation>

<csw:PropertyDefinitions>
<csw:PropertyDefinitionSet name="Basic Properties">
<csw:PropertyDefinition name="Enabled" type="boolean">
<csw:Description>If set to true, this step is enabled</csw:Description>
<csw:DefaultValue>
<csw:Absolute>true</csw:Absolute>
</csw:DefaultValue>
</csw:PropertyDefinition>
</csw:PropertyDefinitionSet>
</csw:PropertyDefinitions>
</csw:StepTemplate>

カスタム・ステップのデプロイ

カスタム・ステップをデプロイするには、jarファイルをデプロイし、ステップ・テンプレートをアップロードする必要があります。 jarファイルをデプロイする場合、$OWSM_HOME/lib/customディレクトリに格納します。 Oracle WSMステップを複数のゲートウェイまたはエージェント、あるいはその両方にデプロイできるので、ポリシー・パイプラインが実施される各サーバーでこのス テップを実行する必要があります。 実行後、アプリケーション・サーバーを再起動する必要があります。

次に、ポリシー・テンプレートをアップロードする必要があります。 これを実行するには、Oracle Web Services Managerコンソールに最初にログインし、実施ポイントの横の「 Steps」リンクをクリックします。 「 Add New Step」ボタンをクリックして、XMLステップ・テンプレートをアップロードします。

再度、実施ポイントごとにこのステップを実行する必要があります。 これで、このステップをポリシー・パイプラインに追加できます。

カスタム・ステップ開発のヒント

カスタム・ステップを構築する障害の1つに、コード-デプロイ-テストといったサイクルがあります。 通常、カスタム・ステップを変更するには、新しいクラスを選択するためにOracle Containers for J2EE(OC4J)を再起動する必要があります。 完全なOracle SOA Suiteがインストールされている場合、多くの再起動を待機する場合があります。

これを高速化するには、Java Platform Debugging Architecture(JPDA)を使用します。 これによって、いくつかの利点があるOC4JにOracle JDeveloperを接続できます。 最初に、カスタム・ステップの実行中に、コードをステップ実行して変数を表示できます。 次にもっとも重要な点として、ホット・スワップ・コードのデプロイを通じてOracle JDeveloperのコードを変更できます。Oracle JDeveloperはコードをJVMに送信し、コードを使用してすぐに開始されます。 jarの構築、カスタムlibディレクトリへの転送、およびサーバーの再起動の速度が大幅に向上します。

これを有効にするには、JPDAデバッガをリスニングするOC4Jを最初に構成する必要があります。

  1. Oracle Enterprise Manager(http://{hostname}:{port}/em)を開いて、oc4j_soaインスタンスをクリックします。
  2. Administration」タブをクリックします。
  3. Server Properties」をクリックします。
  4. コマンドライン・オプションに、次の行を追加します。 -Xrunjdwp:transport=dt_socket,server=y,address=9901,suspend=n.
  5. Apply」をクリックして、OC4Jサーバーを再起動します。

次に、リモートでデバッグするためにOracle JDeveloperプロジェクトを構成する必要があります。
  1. プロジェクトを右クリックして、「 Project Properties」を選択します。
  2. Run/Debug」オプションをクリックします。
  3. デフォルト実行構成をクリックして、「 Edit...」をクリックします。
  4. Launch Settingsオプションの Remote Debugging and Profilingチェック・ボックスが選択されていることを確認します。
  5. Debug」→「 Remote」オプションを選択しま す。
  6. 次のようにオプションを入力します。

  7. OK」をクリックします。
OC4Jインスタンスに接続し、プロジェクトを右クリックして、「 Start Remote Debugger」を選 択します。

Oracle JDeveloperがOC4Jに接続されます。コードのブレーク・ポイントを設置してサービスを実行すると、実行が停止されるのでコードをステップ実行 できます。 また、変更をおこなって[Alt]+[Shift]+[F9](コンパイル)を押すと、コードがJVMに送信されるので再テストを実行できます。

この例の完全なソースは、 サ ンプル・ダウンロードzipファイルにあります。

結論

Webサービスの開発は複雑です。 さらに複雑なのは、一般的なコードを再利用して一般的な機能を抽象化できるWebサービスの実装の設計です。 Decoratorパターンは、コストのかかるコード/テスト/デプロイといったサイクルを実行せずに、サービスに機能を適用する柔軟な方法です。 カスタム・ステップを開発してポリシーへデプロイすることで、ボイラープレート・コードではなく、ビジネス機能に開発者が集中できるようになると、Web サービス・プロジェクトが簡素化されます。


Jason Jonesjason.jones@zirous.com)氏は、 OracleパートナーであるZirousのシステム設計者です。 Jason Jones氏は、Enterprise JavaおよびSOAソリューションを専門としており、オラクルのSOA顧客諮問委員会のメンバーおよびOracle ACE Directorです。また、Java、SOA、およびOracleに関する ブログを開設しています。