18 ADF Webサービス・プロキシの使用

Webサービスを使用するほとんどのアプリケーションには、関連するWeb Services Description Language (WSDL)およびXMLスキーマ定義(XSD)ドキュメントから生成されたプロキシ・クラスが用意されています。
ADFアプリケーションとADF以外のアプリケーションのどちらを構築していようとも、Oracle JDeveloperまたはOracle Enterprise Pack for Eclipse (OEPE)を使用して、ウィザード・ベースのインタフェースを介してJAX-WSプロキシを作成できます。プロキシの作成により、Webサービスにアクセスするための目的のJavaクラスが生成されます。大部分が自動化されたこのプロセスにより、おそらく数百はある退屈なプログラミング操作を実行しなくてすむようになります。これらの操作を手動で行っていたら、アプリケーションの背後にある実際のビジネス上のニーズに対応するためのアプリケーション・コンポーネントの設計と開発が遅れることになります。

静的または動的アプローチを使用するプロキシを構築することにより、Oracle Sales Cloudおよび他のOracle SaaSアプリケーションのSOAP Webサービスと対話できます。これらのプロキシにより、SOAPによって公開されるJava API for XML Web Services (JAX-WS)オブジェクトがJavaクラスに変換され、これらのクラスがアプリケーションで使用できるようになります。各アプローチには長所と短所があります。

前提条件

このドキュメントに示される開発の例を実行する前に、以下の点に注意してください。

  • 必ずJavaバージョン6 (SEまたはEE)を実行してください。

  • 正しいバージョンのOracle JDeveloper IDEがインストールされていることを確認してください。

    注意:

    Oracle JDeveloper IDEを使用してOracle Cloudで開発するには、Oracle JDeveloper Studio Editionバージョン11.1.1.7.1を使用する必要があります。これより新しいバージョンを含む他のOracle JDeveloperのバージョンの場合、Oracle Cloudの統合機能をサポートしていません。Oracle JDeveloper Studio Edition 11.1.1.7.1をOracle Technology Network (http://www.oracle.com/technetwork/developer-tools/jdev/downloads/jdeveloer111171-2183166.html)からダウンロードしてください。

静的プロキシについて

設計時または開発時にWebサービス・クライアントをコンパイルおよびバインディングする場合、静的プロキシを使用します。これにより、サービスWSDLによって公開される(Javaクラスに便利にマップされる)データ・オブジェクト・タイプのスナップショットがIDEに抽出されます。

このインポートの一部として、IDEにより、使用可能なWebサービス・クライアント・プロキシ用の静的スタブが生成されます。生成された静的スタブ・クライアントのソース・コードは、個別の特定時点(つまり、インポートを実行した時点)における特定のサービス実装を表しています。

プロキシの専用プロジェクトの使用

Webサービス・プロキシを設計および実装する場合、Oracleでは、採り入れるべき特定のベスト・プラクティスに関するアドバイスを提供しています。

プロキシ(静的と動的の両方)は、独自の専用プロジェクトまたはJARファイル内に作成する必要があります。専用プロジェクトまたはJARファイルを使用することにより、必要に応じて、メイン・プロジェクトに影響を及ぼさずに静的プロキシを再生成できます。ウィザード・ベースのツール(Oracle JDeveloperに用意されているものなど)を使用するか、コマンド・ライン・ユーティリティを使用することにより、静的プロキシを生成できます。

同じソース(Oracle Sales Cloudなど)から複数のWebサービスを使用する場合、異なるWSDLから作成されるJavaおよびXMLアーティファクトに関するネームスペースの競合または重複が生じる可能性があります。Webサービスごとに異なるプロキシ・プロジェクトを使用すると、使用対象の各サービスを隔てる明確な境界を維持し、別の方法では一意のパッケージ構造を使用することによって回避される偶発的な重複や競合を防止しやすくなります。プロキシごとに専用のプロジェクトを使用することによって得られるもう1つのメリットは、分散開発環境で、複数の異なる開発チームが他のチームに干渉することなく、個々のプロキシ・クラスに対して同時に作業できる点です。

静的プロキシを使用したアプリケーションの構築(作業例)

この例では、JDeveloperに静的プロキシを実装することにより、JAX-WSオブジェクトを受け入れ、これらをJavaアプリケーションで使用します。

Webサービス・プロキシを使用してアプリケーションを構築する手順:
  1. まだアプリケーションを作成していない場合、アプリケーションを作成します。下図は、Fusion Webアプリケーション・タイプのアプリケーションを示しています。
  2. アプリケーションの名前を入力し、「Next」をクリックします。
  3. Modelプロジェクトの場合、「プロジェクト・テクノロジ」タブで、「Oracle Cloud」および「Webサービス」「使用可能」から「選択済」に移動します。


    ヒント:

    「プロキシの専用プロジェクトの使用」で説明されているプロキシ・プロジェクトのベスト・プラクティスを参照してください。
  4. ViewControlプロジェクトを作成するには、 Fusion Webアプリケーション(ADF)の作成ウィザードのステップ3~5のデフォルト設定を受け入れて、Fusion ADF Webアプリケーションを作成し、「終了」をクリックします。
  5. Modelプロジェクト内に新しいWebサービス・プロキシを作成します。「すべてのテクノロジ」タブで、「ビジネス層」カテゴリを展開し、「Webサービス」を選択します。次に、「Webサービス・プロキシ」を選択し、「OK」をクリックします。
  6. JAX-WSを選択し、「Next」をクリックします。
  7. 起動するSOAP WebサービスのWSDLドキュメントのURLを入力し、「Next」をクリックします。
  8. パッケージ名を入力します。Javaの標準パッケージのネーミング規則に準拠するパッケージ名を入力し、パッケージ仕様にxxx.yyy.zzz.proxyおよびxxx.yyy.zzz.proxy.typesを追加します。
    たとえば、oracle.sample.salesparty.proxyoracle.sample.salesparty.proxy.typesのように入力します。
  9. サービスが非同期リクエスト/レスポンスをサポートしているときに、非同期インタフェースに対してクラスを生成する必要がある場合、チェック・ボックスが表示されます。非同期機能を使用する予定がない場合は、「非同期として生成」の選択を解除します。「Next」をクリックします。
  10. ポートを選択します。
  11. Webサービスの非同期機能を使用する予定がない場合は、次の画面で、「非同期メソッドを生成しない」を選択します。
  12. 次の画面で、使用するセキュリティ・ポリシーを選択します。指定したWSDLに基づいてクライアント・ポリシーのリストが生成されます。推奨される手法は、可能な場合はSAMLに基づくポリシーを使用する手法です。または、ユーザー名トークンまたはHTTP基本ポリシーを使用することもできます。
    • SAMLアイデンティティは、関連付けられたJCS - SaaS ExtensionインスタンスとOracle Sales Cloudインスタンスの間で自動的に使用可能になります。SAMLトークン交換ベースのクライアント・ポリシーを使用するには、oracle/wss_saml_token_bearer_over_ssl_client_policyまたはoracle/wss11_saml_token_with_message_protection_client_policyの1つを選択します。
    • オプションで、かわりに(デバッグ用、他のOracle SaaSオファリングからのJAX-WSサービスへのアクセス用、または関連付けられていないサービスを使用する場合の)ユーザー名トークン・ベースのクライアント・ポリシーを使用する場合、「プロパティのオーバーライド」をクリックしてユーザー名およびパスワードを設定できます。この場合、username_token_over_ssl_client_policyが選択されます。

    アカウントの設定の詳細は、「JCS - SaaS Extensionのサブスクリプションの取得」を参照してください。

    プロキシの正しいセキュリティ・ポリシーの選択の詳細は、「JCS - SaaS ExtensionとOracle SaaSの相互作用の認可戦略」を参照してください。

    注意:

    後でクライアント・ポリシーを変更することも、ポリシーをプログラムでオーバーライドすることもできます。

「終了」をクリックした後、プロキシが生成されます。プロキシを右クリックすると、いつでもプロパティを変更できます。また、プロキシをいつでも再生成できます。「Webサービス・プロキシの再生成」を参照してください。

プロキシ・プロジェクトのリストには、「Add your code to call the desired methods」というテキストが記載されたファイル<port name>Client.java (SalesPartyServiceSoapHttpPortClient.javaなど)も表示されています。

注意:

プロキシが再生成される場合、クライアントの変更が上書きされるため、プロキシ・クライアント・クラスにカスタム・コードを埋め込むことはお薦めできません。また、生成されたプロキシに含まれる、操作に便利なパラメータが多すぎる場合もあります。かわりに、ファサードのJavaクラスを作成し、独自のメソッドを作成する必要があります。ファサードを使用すると、ペイロード全体を操作する必要なく、操作対象の入力フィールドと出力フィールドを制御できます。作成するメソッドは、実際のプロキシ・クラスの周辺のラッパーであり、ユースケースに最も関連のあるメソッドとパラメータのみが公開されています。

単純な静的プロキシ・ファサードの作成方法は、「Webサービス・プロキシのファサードの作成(SalesPartyサービスの例)」を参照してください。

Webサービス・プロキシのファサードの作成(SalesPartyサービスの例)

プロキシを操作する場合はたいてい、ファサードを作成して、操作するフィールドの数を制限する方が有益です。

たとえば、Oracle Sales Cloudで、https://<<Your_Oracle_Sales_Cloud_URL>/>/crmCommonSalesParties/SalesPartyService?WSDLに定義されているSalesParty Webサービスを操作する場合、メッセージ・ペイロード全体を操作する必要はありません。作業対象を特定のフィールドのみに制限できます。これらのメソッドは、実際のプロキシ・クラスの周辺のラッパーであり、ユースケースに最も関連のあるメソッドとパラメータのみが公開されています。
この項の後半で示すファサードは、SalesPartyプロキシおよびfind操作を使用する方法を示しています。
このファサードでは、別のプロジェクトを作成し、そのプロジェクトにJavaクラスを追加し、テスト・コードが実装されているプロジェクトがプロキシ・プロジェクトに依存していることを確認する必要があります。複数の開発者が同じプロキシを使用して作業している場合、この区別が役に立ちます。
ファサードを作成する手順:
  1. ワークスペース内で2番目のプロジェクトを作成します。この例では、「Facade」という名前を付けます。
  2. プロジェクトのプロパティを編集します。依存性セクションで、「ビルド出力」が選択されていることを確認します。
  3. SalesPartyFacadeという名前のJavaクラスを追加します。
  4. この高度な問合せサンプルにコードを追加します。

次の例では、findCriteriaを使用してXMLリクエスト・ペイロードを構築します。getSalesPartyListメソッドは、ユーザーから入力としてstartsWithのみを受け入れ、findCriteriaを構築し、サービスを実行し、販売パーティ・レコードのリストを返します。

// Copyright 2015, Oracle and/or its affiliates.All rights reserved package oracle.cloud.sampleapps.salesmerchtracker.model.proxy.facade; import com.sun.xml.ws.developer.WSBindingProvider; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.SalesPartyService; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.SalesPartyService_Service; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.ServiceException; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.ChildFindCriteria; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.Conjunction; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.FindCriteria; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.FindSalesParty; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.SalesParty; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.ViewCriteria; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.ViewCriteriaItem; import oracle.cloud.sampleapps.salesmerchtracker.model.proxy.types.ViewCriteriaRow; import weblogic.wsee.jws.jaxws.owsm.SecurityPolicyFeature; public class SalesPartyServiceFacade { private SalesPartyService_Service salesPartyService_Service; private List<SimplifiedSalesParty> SimplifiedSalesParties = new ArrayList<SimplifiedSalesParty>(); private long partyId; private String partyName; public List<SimplifiedSalesParty> getSalesPartyList(String startsWith) throws ServiceException { List<SalesParty> SalesParties; FindCriteria findCriteria = createFindCriteria(startsWith); // You can change your security policy here SecurityPolicyFeature[] securityFeatures = // new SecurityPolicyFeature[] { new SecurityPolicyFeature("oracle/wss_username_token_over_ssl_client_policy") }; new SecurityPolicyFeature[] { new SecurityPolicyFeature("oracle/wss_saml_token_bearer_over_ssl_client_policy") }; //salesPartyService_Service = new SalesPartyService_Service(); //to accomodate for new URL everytime, you will chanage the URL here salesPartyService_Service = new SalesPartyService_Service(); SalesPartyService salesPartyService = salesPartyService_Service.getSalesPartyServiceSoapHttpPort(securityFeatures); // SalesPartyService salesPartyService = salesPartyService_Service.getSalesPartyServiceSoapHttpPort(); // If using wss_username_token_over_ssl_client_policy uncomment the three lines below to provide the username/password // WSBindingProvider wsbp = (WSBindingProvider)salesPartyService; // wsbp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY,"your user name"); // wsbp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,"your password"); FindSalesParty fSalesParty= new FindSalesParty(); fSalesParty.setFindCriteria(findCriteria); try{ SalesParties = salesPartyService.findSalesParty(fSalesParty).getResult(); if (!SalesParties.isEmpty()) { for (SalesParty sp: SalesParties) { partyName = sp.getPartyName().getValue(); partyId = sp.getPartyId(); SimplifiedSalesParty s1 = new SimplifiedSalesParty(partyId, partyName); SimplifiedSalesParties.add(s1); } } } catch (Exception e) { e.printStackTrace(); } return SimplifiedSalesParties; } private static FindCriteria createFindCriteria(String startsWith) { FindCriteria findCriteria = new FindCriteria(); ChildFindCriteria childFindCriteria = new ChildFindCriteria(); findCriteria.setFetchStart(0); findCriteria.setFetchSize(10); ViewCriteria filter = new ViewCriteria(); ViewCriteriaRow group1 = new ViewCriteriaRow(); ViewCriteriaItem item1 = new ViewCriteriaItem(); item1.setUpperCaseCompare(true); item1.setAttribute("PartyName"); item1.setOperator("STARTSWITH"); item1.getValue().add(startsWith); ViewCriteriaItem item2 = new ViewCriteriaItem(); item2.setAttribute("PartyType"); item2.setOperator("="); item2.getValue().add("ORGANIZATION"); group1.getItem().add(item1); group1.getItem().add(item2); group1.setConjunction(Conjunction.AND); filter.getGroup().add(group1); findCriteria.setFilter(filter); findCriteria.getFindAttribute().add("PartyId"); findCriteria.getFindAttribute().add("PartyName"); // findCriteria.getFindAttribute().add("OrganizationParty"); /* childFindCriteria.setChildAttrName("OrganizationParty"); childFindCriteria.getFindAttribute().add("Address1"); childFindCriteria.getFindAttribute().add("City"); childFindCriteria.getFindAttribute().add("Country"); findCriteria.getChildFindCriteria().add(childFindCriteria); */ return findCriteria; } }

このプロキシはSAMLクライアント・ポリシーを使用するよう構成されているため、このファサードはSAMLポリシーを自動的に使用します。ただし、(テストのため、または関連付けられていないサービスを操作する場合など) SAMLポリシーのかわりにユーザー名トークンを使用する場合、上記のように、SAMLポリシー行をコメント・アウトし、ユーザー名トークン・ポリシー行のコメント・アウトを解除します。

ユーザー名トークン・ポリシーが機能するには、ファサードにユーザー名とパスワードを指定する必要があります。これを行うには、上記のコメント付きコードに示すように、WSBindingProviderを使用して、USERNAME_PROPERTYPASSWORD_PROPERTYをそれぞれ使用してユーザー名とパスワードをオーバーライドできます。

IDE内でユーザー名トークン・ポリシーをテストする場合、単純なテスト・クラスを使用してこのファサードをテストできます。次に例を示します。

String filter = "Art"; SalesPartyFacade spf = new SalesPartyFacade(); List<SalesParty> salesparties = spf.getSalesPartyList(filter); for (SalesParty sp: salesparties) {System.out.print("Party Name = " + sp.getPartyName().getValue()+"\n"); System.out.print("Address1 = " + sp.getOrganizationParty().get(0).getAddress1().getValue()+"\n"); System.out.print("City = " + sp.getOrganizationParty().get(0).getCity().getValue()+"\n"); System.out.print("Country = " + sp.getOrganizationParty().get(0).getCountry().getValue()+"\n"); System.out.println("\n\n"); }
プロキシのセキュリティ・ポリシーの詳細は、「Oracle Sales Cloud SOAP Webサービスのクライアントのセキュリティ・ポリシーの選択」を参照してください。

Webサービス・プロキシの再生成

静的プロキシの重要な欠点は、開発者が(Oracle Sales Cloudをカスタマイズする場合などに)フィールドを変更したりサービスにフィールドを追加したりすることで、WSDLを変更する際に実証されます。静的プロキシが再生成されるまでは、SOAP Webサービス・コールのペイロードに変更が現在含まれていても、コードでこれらの変更を使用することはできません。アプリケーションでのこれらの予期しない変更への対処方法によっては、レスポンス・ペイロードへの変更が著しい場合、追加フィールドが単純に無視されたり、互換性のないデータの発生が原因でエラーがスローされたりする可能性があります。
JDeveloperでプロキシを再生成する手順:
  1. JDeveloperでアプリケーションが開かれた状態で、「プロジェクト」ペインで、プロキシを右クリックし、コンテキスト・メニューから「プロパティ」を選択します。
    プロキシ・エディタが開きます。
  2. プロキシ・エディタダイアログで、「WSDLドキュメントURL」をOracle SaaSサービス環境の実際のWSDL URLに変更します。「WSDLをプロジェクトにコピー」の選択が解除されていることを確認します。
  3. プロキシ・エディタ・ダイアログの左側のペインで、「ポート・エンドポイント」を選択します。

    「WSDLのフェッチ中」「モデルの作成中」などのタイトルを持つ一連のポップアップ進捗状況インジケータが開きます。WSDLの解析プロセスが完了するまで数分かかる場合があります。例の販売パーティWSDLは非常に大きく複雑であるため、処理により多くの時間がかかります。より単純な別のWSDLを使用している場合、処理にかかる時間が短くなる可能性があります。

    WSDLの処理が終了したら、「ポート・エンドポイント」を選択すると、プロキシ・エディタ・ダイアログにエンドポイントが表示されます。



  4. 「OK」をクリックします。

    検証プロセスが実行された後、複数のプロキシ関連のJavaファイルが作成されます。

静的Webサービス・プロキシが再生成されました。

動的プロキシについて

通常、動的プロキシは、開発がより複雑ですが、静的プロキシと比較して一定のメリットを備えています。

動的プロキシの場合、開発(つまり、clientgenの使用)時にWSDLコンテンツはコンパイルされません。かわりに、実行時に、アプリケーションがWSDLペイロードを動的に取得および解釈してから、検出されたデータ構造を操作するコールの構築に進みます。標準オブジェクトに追加されたカスタム・オブジェクトまたはカスタム属性の作成を通じてサーバー側のインタフェースが変更される場合、動的プロキシ・クライアントはより強固になります。

Java仕様によると、動的プロキシ・クラスは、クラスのインスタンス上のインタフェースの1つを介したメソッドの起動が一意のインスタンスを介して別のオブジェクトにエンコードおよびディスパッチされるように、実行時に指定されるインタフェースのリストを実装するクラスです。このため、動的プロキシ・クラスを使用すると、コンパイル時ツールなどを使用してプロキシ・クラスを事前に生成する必要なく、インタフェースのリスト用のタイプセーフのプロキシ・オブジェクトを作成できます。動的プロキシ・クラスのインスタンス上のメソッドの起動は、インスタンスの起動ハンドラ内の単一のメソッドにディスパッチされ、これらは、起動されたメソッド、および引数を含むオブジェクト・タイプの配列を識別するjava.lang.reflect.Methodオブジェクトを使用してエンコードされます。動的プロキシ・クラスは、インタフェースAPIを表すオブジェクトにおける起動のタイプセーフの反映型ディスパッチを提供する必要がある、アプリケーションまたはライブラリにとって有効です。

動的プロキシは、設計および実装がより複雑になる可能性があります。ただし、これらは、接続管理、パフォーマンス監視、トランザクション管理などの複雑な要件に対応し、高度な制御を行うことができます。

動的プロキシの作成

動的プロキシは、javax.xml.ws.ServiceオブジェクトとともにService.createメソッドを使用して実装できます。

動的プロキシ・クライアントを作成するステップを次に示します。詳細は、javax.xml.ws.Service Javadoc (http://docs.oracle.com/javaee/6/api/javax/xml/ws/Service.html)を参照してください。
  1. Service.createメソッドを使用してjavax.xml.ws.Serviceインスタンスを作成します。
    サービス名、およびオプションでWSDLドキュメントの場所を渡します。メソッドの詳細は、次のとおりです。
    public static Service create (QName serviceName) throws javax.xml.ws.WebServiceException {} public static Service create (URL wsdlDocumentLocation, QName serviceName) throws javax.xml.ws.WebServiceException {}
    次に例を示します。
    URL wsdlLocation = new URL("http://example.org/my.wsdl"); QName serviceName = new QName("http://example.org/sample", "MyService"); Service s = Service.create(wsdlLocation, serviceName);
  2. Service.getPortメソッドを使用して、プロキシ・スタブを作成します。このスタブを使用して、ターゲット・サービス・エンドポイントに対する操作を起動できます。
    サービス・エンドポイント・インタフェース(SEI)、およびオプションでWSDLサービス記述内のポート名を渡す必要があります。メソッドの詳細は、次のとおりです。
    public <T> T getPort(QName portName, Class<T> serviceEndpointInterface) throws javax.xml.ws.WebServiceException {} public <T> T getPort(Class<T> serviceEndpointInterface) throws javax.xml.ws.WebServiceException {}
    次に例を示します。
    MyPort port = s.getPort(MyPort.class);