17 MAFアプリケーションでのWebサービスの使用方法

この章では、MAFアプリケーションからREST Webサービスにアクセスする方法について説明します。

この章の内容は次のとおりです。

MAFアプリケーションでのWebサービス使用の概要

MAFは、MAFアプリケーションにおけるJSONオブジェクトを使用したREST Webサービスの消費をサポートしています。MAFでこのタイプのWebサービスが他のWebサービスよりも推奨される理由は、生成するペイロードが通常は少ないため、アプリケーションと、アプリケーションがアクセスするサービスとの間の通信のレスポンス時間が短くなるからです。

MAFアプリケーションでREST-JSON Webサービスを使用する場合は、WebサービスのURLエンド・ポイントへの接続をアプリケーションで構成する必要があります。MAFは、このエンド・ポイントをアプリケーションのconnections.xmlファイルに格納します。また、connections.xmlに構成した値をとるアダプタ(RESTServiceAdapter)を記述し、それを使用して、Webサービスに送信するリクエストURIを構築します。Webサービスが返すデータをモデル化するJavaクラスを記述する必要もあります。これらのクラスを使用して、WebサービスがアクセスするデータにアプリケーションのAMXページをバインドするデータ・コントロールを生成します。アプリケーションが保護されたWebサービスにアクセスする場合は、そのWebサービスのURLエンド・ポイントへの接続にセキュリティ・ポリシーを関連付ける必要があります。アプリケーションが、自社ファイアウォールの外部でホストされているサービスにアクセスする必要がある場合は、アプリケーションのmaf.propertiesファイルにエントリを構成する必要もあります。

注意:

RESTServiceAdapterを記述するかわりに、MAFにより提供される、REST Webサービスにアクセスするクライアント・データ・モデルを生成するためのデザインタイム・サポートを使用します。「MAFアプリケーションでのクライアント・データ・モデルの作成」を参照してください。

WorkBetterサンプル・アプリケーションは、RESTServiceAdapterを使用してRESTサービスをプログラムで消費する例を示します。WorkBetterサンプル・アプリケーションのソース・コードにアクセスする方法の詳細は、「サンプルのMAFアプリケーション」を参照してください。

WebサービスにアクセスするためのRESTサービス・アダプタの作成

RestServiceAdapterを使用して、RESTコールにより送信されたデータにアクセスし、Webサービス操作の実行をトリガーします。oracle.maf.api.dc.ws.restパッケージからのRestServiceAdapterFactory.createRestServiceAdapter() APIは、RestServiceAdapterを実装するアダプタを作成します。

サービスのURLエンド・ポイントへの接続がconnections.xmlファイルに存在することを確認し、次の例で示すようにコードをBeanクラスに追加します。

Mobile Cloud Serviceに診断情報を送信するアダプタを作成する必要がある場合は、「Oracle Mobile Cloud Serviceへの診断情報の送信」で説明しているように、RestServiceAdapterFactory.createMcsRestServiceAdapter() APIを使用します。

RestServiceAdapterFactoryおよびRestServiceAdapterの詳細は、Oracle Mobile Application Framework Java APIリファレンスを参照してください。

....
import oracle.maf.api.dc.ws.rest.RestServiceAdapterFactory;
import oracle.maf.api.dc.ws.rest.RestServiceAdapter;
....
RestServiceAdapterFactory factory = RestServiceAdapterFactory.newFactory();
RestServiceAdapter restServiceAdapter = factory.createRestServiceAdapter();

// Clear any previously set request properties, if any
restServiceAdapter.clearRequestProperties();

// Set the connection name
restServiceAdapter.setConnectionName("RestServerEndpoint");

// Specify the type of request
restServiceAdapter.setRequestMethod(RestServiceAdapter.REQUEST_TYPE_GET);

// Specify the number of retries
restServiceAdapter.setRetryLimit(0);

// Set the URI which is defined after the endpoint in the connections.xml.
// The request is the endpoint + the URI being set
restServiceAdapter.setRequestURI("/WebService/Departments/100");

// Following code snippets demonstrate how to use getHttpURLConnection to initialize 
// and return java.net.HttpURLConnection or javax.net.ssl.HttpsURLConnection.
// The getHttpURLConnection injects all security headers based on the security policy 
// configured for the connection used to create this adapter. 
// The supported policies depend on the authentication server type that you use (HTTP Basic, OAuth, or Web SSO). 

// Get the type of request
String requestMethod = RestServiceAdapter.REQUEST_TYPE_GET;

// Get the connection end point from connections.xml
String requestEndPoint = restServiceAdapter.getConnectionEndPoint("RestServerEndpoint");

// Get the URI which is defined after the end point
String requestURI = "/xml/" + someIpAddress;

// The request is the end point + the URI being set
String request = requestEndPoint + requestURI;

// Specify some custom request headers
HashMap httpHeadersValue = new HashMap();
httpHeadersValue.put("Accept-Language", "en-US");
httpHeadersValue.put("My-Custom-Header-Item", "CustomItem1");

// Get the connection
HttpURLConnection connection = restServiceAdapter.getHttpURLConnection(requestMethod, request, httpHeadersValue);

String response = "";

// Execute SEND and RECEIVE operation
try {
   // For GET request, there is no payload
   response = restServiceAdapter.send("");
}
catch (Exception e) {
   e.printStackTrace();
}

次の例は、POSTリクエストに対するRestServiceAdapterの使用方法を示しています。

String id = "111";
String name = "TestName111";
String location = "TestLocation111";
....

restServiceAdapter.clearRequestProperties();
restServiceAdapter.setConnectionName("RestServerEndpoint");
restServiceAdapter.setRequestMethod(RestServiceAdapter.REQUEST_TYPE_POST);
restServiceAdapter.setRetryLimit(0);
restServiceAdapter.setRequestURI("/WebService/Departments");

String response = "";

// Execute SEND and RECEIVE operation
try {
   String postData = makeDepartmentPost("DEPT", id, name, location);
   response = restServiceAdapter.send(postData);
}
catch (Exception e) {
   e.printStackTrace();
}
System.out.println("The response is:  " + response);

private String makeDepartmentPost(String rootName, String id, 
                                  String name, String location) {
   String ret = "<" + rootName + ">";
   ret += "<DEPTID>" + id + "</DEPTID>";
   ret += "<NAME>" + name + "</NAME>";
   ret += "<LOCATION>" + location + "</LOCATION>";
   ret += "</" + rootName + ">";
   return ret;
}

次の例は、PUTリクエストに対するRestServiceAdapterの使用方法を示しています。

String id = "111";
String name = "TestName111";
String location = "TestLocation111";

....

restServiceAdapter.clearRequestProperties();
restServiceAdapter.setConnectionName("RestServerEndpoint");
restServiceAdapter.setRequestMethod(RestServiceAdapter.REQUEST_TYPE_PUT);
restServiceAdapter.setRetryLimit(0);
restServiceAdapter.setRequestURI("/WebService/Departments");

String response = "";

// Execute SEND and RECEIVE operation
try {
   String putData = makeDepartmentPut("DEPT", id, name, location);
   response = restServiceAdapter.send(putData);
}
catch (Exception e) {
   e.printStackTrace();
}
System.out.println("The response is:  " + response);

private String makeDepartmentPut(String rootName, String id, 
                                  String name, String location) {
   String ret = "<" + rootName + ">";
   ret += "<DEPTID>" + id + "</DEPTID>";
   ret += "<NAME>" + name + "</NAME>";
   ret += "<LOCATION>" + location + "</LOCATION>";
   ret += "</" + rootName + ">";
   return ret;
}

次の例は、DELETEリクエストに対するRestServiceAdapterの使用方法を示しています。

....

restServiceAdapter.clearRequestProperties();
restServiceAdapter.setConnectionName("RestServerEndpoint");
restServiceAdapter.setRequestMethod(RestServiceAdapter.REQUEST_TYPE_DELETE);
restServiceAdapter.setRetryLimit(0);
restServiceAdapter.setRequestURI("/WebService/Departments/44");

String response = "";

// Execute SEND and RECEIVE operation
try {
   // For DELETE request, there is no payload
   response = restServiceAdapter.send("");
}
catch (Exception e) {
   e.printStackTrace();
}

System.out.println("The response is:  " + response);

RestServiceAdapterを使用する場合は、AcceptヘッダーとContent-Typeヘッダーを設定して、MIMEタイプの不一致が原因でリクエストおよびレスポンスのペイロードが無効と判断されないようにします。

注意:

REST Webサービス・アダプタでは、MAFアプリケーション上の文字セットとしてUTF-8のみをサポートします。UTF-8は、アダプタのプログラムに埋め込まれています。

入力および出力ストリームへのアクセス

RestServiceAdapterを使用して、java.net.HttpURLConnectionを取得し、カスタマイズしたり、HttpURLConnectionからのデータの読取りおよび書込み、さらにはサーバーへのアップロードが可能な接続の入力および出力ストリームと対話したりすることができます。

次の例では、指定されたリクエスト・メソッド、リクエストおよびHTTPヘッダー値を使用して、HttpURLConnectionを初期化して返しています。また、資格証明ストアから基本認証をリクエスト・ヘッダーに挿入し、入力ストリームを取得し、接続を閉じます。

....
// Get the connection
HttpURLConnection httpURLConnection = 
   restServiceAdapter.getHttpURLConnection(requestMethod, request, httpHeadersValue);

// Get the input stream
InputStream inputStream = restServiceAdapter.getInputStream(connection);

// Define data
ByteArrayOutputStream byStream = new ByteArrayOutputStream();

int res = 0;
int bufsize = 0, bufread = 0;

byte[] data = (bufsize > 0) ? new byte[bufsize] : new byte[1024];

// Use the input stream to read data
while ((res = inputStream.read(data)) > 0) {
   byStream.write(data, 0, res);
   bufread = bufread + res;
}
data = byStream.toByteArray();

// Use data 
...

restServiceAdapter.close(connection);
...

非テキスト・レスポンスのサポート

Webサービス・コールから受け取ったバイナリ(非テキスト)レスポンスを処理するには、RestServiceAdapterを使用できます。これらのレスポンスには、PDFファイルやビデオ・ファイルなど、どの種類のバイナリ・データでも含めることができます。使用するRestServiceAdapterメソッドはsendReceiveです。

次の例は、RESTサーバーにファイルのリクエストを送信し、ファイルをディスクに保存する方法を示しています。

RestServiceAdapterFactory factory = RestServiceAdapterFactory.newFactory();
RestServiceAdapter restServiceAdapter = factory.createRestServiceAdapter();

restServiceAdapter.clearRequestProperties();
restServiceAdapter.setConnectionName("JagRestServerEndpoint");
restServiceAdapter.setRequestMethod(RestServiceAdapter.REQUEST_TYPE_GET);
restServiceAdapter.setRetryLimit(0);
restServiceAdapter.setRequestURI("/ftaServer/v1/kpis/123/related/1");

// Set credentials needed to access the REST server 
String theUsername = "hr_spec_all";
String thePassword = "Welcome1"; 
String userPassword = theUsername + ":" + thePassword;
String encoding = new sun.misc.BASE64Encoder().encode(userPassword.getBytes());

restServiceAdapter.addRequestProperty("Authorization", "Basic " + encoding);

// Execute the SEND and RECEIVE operation.
// Since it is a GET request, there is no payload.
try {
   this.responseRaw = restServiceAdapter.sendReceive("");
}
catch (Exception e) {
   e.printStackTrace();
}
System.out.println("The response is:  " + this.responseRaw);

// Write the response to a file
writeByteArrayToFile(this.responseRaw);

次の例は、前の例のコードによってコールされたメソッドを示しています。このメソッドでは、ファイルに対するbyte[]レスポンスをディスクに保存します。

public void writeByteArrayToFile(byte[] fileContent) {
   BufferedOutputStream bos = null;
   try {
      FileOutputStream fos = new FileOutputStream(new File(fileToSavePath));
      bos = new BufferedOutputStream(fos);
      // Write the byte [] to a file 
      System.out.println("Writing byte array to file");
      bos.write(fileContent);
      System.out.println("File written");
   }
   catch(FileNotFoundException fnfe) {
      System.out.println("Specified file not found" + fnfe);
   }
   catch (IOException ioe) {
      System.out.println("Error while writing file" + ioe);
   }
   finally {
      if(bos != null) {
         try {
            // Flush the BufferedOutputStream
            bos.flush();
            // Close the BufferedOutputStream
            bos.close();
         }
         catch (Exception e) {
         }
      }
   }
}

セキュアなWebサービスへのアクセス

MAFでは保護されたWebサービスと保護されていないWebサービスの両方がサポートされます。REST Webサービスが保護されている場合、REST Webサービスをサポートする事前定義済のセキュリティ・ポリシーにREST接続を関連付ける必要があります。

表17-1に、REST Webサービスへの接続に関連付けることができる事前定義済のセキュリティ・ポリシーを示します。「Webサービスへのアクセスを有効にする方法」では、事前定義済のセキュリティ・ポリシーに接続を関連付ける方法を説明します。

表17-1 RESTベースのWebサービスでサポートされるセキュリティ・ポリシー

認証タイプ RESTポリシー 説明

HTTP Basic

oracle/wss_http_token_over_ssl_client_policy

このポリシーは、アウトバウンド・クライアント・リクエストのHTTPヘッダーに資格証明を含め、Oracle Platform Security Servicesアイデンティティ・ストアに対してユーザーを認証します。また、このポリシーは、トランスポート・プロトコルがHTTPSであることを検証します。HTTPS以外のトランスポート・プロトコルを介するリクエストは拒否されます。このポリシーは、HTTPベースのクライアントで実行できます。

HTTP Basic

oracle/wss_http_token_client_policy

このポリシーは、アウトバウンド・クライアント・リクエストのHTTPヘッダーに資格証明を含めます。このポリシーは、HTTPベースまたはHTTPSベースのクライアントで実行できます。

HTTP Basic

oracle/wss_http_token_over_ssl_client_policy

このポリシーは、アウトバウンド・クライアント・リクエストのHTTPヘッダーに資格証明を含め、Oracle Platform Security Servicesアイデンティティ・ストアに対してユーザーを認証します。また、このポリシーは、トランスポート・プロトコルがHTTPSであることを検証します。HTTPS以外のトランスポート・プロトコルを介するリクエストは拒否されます。このポリシーは、HTTPベースのクライアントで実行できます。

HTTP Basic

Web SSO

oracle/http_cookie_client_policy

このポリシーは、認証後に取得されたCookieをHTTPリクエスト・ヘッダーに挿入します(例: OAM Webgateリソース)。このポリシーでは、レスポンスCookieも設定します。このポリシーは、RESTベースのクライアントで実行できます。

OAuth

oracle/http_oauth2_token_mobile_client_policy

このポリシーは、エンドポイントとの通信中にBearerトークン(OAuthアクセス・トークン)をHTTPリクエスト・ヘッダーに挿入します。このトークンは、任意のOAuth2サーバーから取得できます。このポリシーは、RESTベースのクライアントで実行できます。

これらのポリシーとその使用方法の詳細は、『Oracle Web Services ManagerによるWebサービスの保護とポリシーの管理』使用する事前定義済ポリシーの決定に関する項と、事前定義済ポリシーに関する項を参照してください。

Webサービスへのアクセスを有効にする方法

Webサービスが保護されており、認証トークンが予想される場合、Webサービスをサポートする事前定義済のセキュリティ・ポリシーにログイン接続を関連付ける必要があります。

RESTベースのWebサービスで使用できる認証タイプ用にサポートされている事前定義済セキュリティ・ポリシーのリストは、「セキュアなWebサービスへのアクセス」を参照してください。maf-application.xml概要エディタの「MAFログイン接続の作成」ダイアログを使用して、ログイン・サーバー接続を作成します。ログイン・サーバー接続の作成の詳細は、「MAFログイン接続の作成方法」を参照してください。

Webサービスにセキュリティ・ポリシーを関連付けるには:

  1. 「アプリケーション」ウィンドウで、「ディスクリプタ」ノード、「ADF META-INF」と開き、maf-application.xmlをダブルクリックします。次に、maf-application.xmlファイルの概要エディタで「セキュリティ - Webサービス・セキュリティ・ポリシー」セクションを展開し、作成済のWebサービス接続を「名前」フィールドで選択します。
  2. 「ログイン・サーバー接続」フィールドで、定義したログイン・サーバー接続を選択します。
  3. 「ポリシー」フィールドで、「ポリシーを編集」アイコン・ボタンをダブルクリックして、「データ・コントロール・ポリシーの編集」ダイアログで現在のWebサービス接続のサービスに関連付けるポリシーを選択して、「OK」をクリックします。
    図17-1は、RESTConnection1およびRESTConnection2に関連付けられているポリシーを示しています。

    「データ・コントロールの編集」ダイアログで選択でき、RESTベースのWebサービスに関連付けることができるセキュリティ・ポリシーのリストは、「セキュアなWebサービスへのアクセス」を参照してください。

    図17-1 Webサービス接続へのセキュリティ・ポリシーの関連付け

    複数のポリシーがRESTサービス接続およびログイン・サーバー接続に関連付けられているmaf-application.xmlの概要エディタの「セキュリティ」ページの「Webサービス・セキュリティ・ポリシー」セクションを表示します。

Webサービスへのアクセスを有効にする場合の処理

JDeveloperは、META-INFディレクトリ内のwsm-assembly.xmlファイルに、Webサービス・ポリシー定義を保存します。

「データ・コントロール・ポリシーの編集」ダイアログ(図17-2を参照)を使用して、REST Webサービスにすでに関連付けられているセキュリティ・ポリシーを表示できます。「プロパティのオーバーライド」をクリックしてダイアログを開き、そのダイアログで、選択したポリシーのオーバーライドが許可されているプロパティの代替値を指定できます。

図17-2 Webサービス・データ・コントロール・ポリシーの編集

セキュリティ・ポリシーのプロパティを上書きできる「データ・コントロール・ポリシーの編集」ダイアログを表示します。

資格証明の挿入に関する必知事項

保護されたWebサービスにおけるユーザーの資格証明は、Webサービス・リクエストの起動時にそのリクエストに動的に挿入されます。

MAFではOracle Web Services Manager (OWSM) Mobile Agentを使用してWebサービス・リクエストによってユーザーIDを伝播させます。

Webサービスを起動する前に、ユーザーは、保護されたMAFアプリケーション機能の呼出しを試みるユーザーによってトリガーされた認証プロンプトに応答する必要があります。ユーザーの資格証明は、資格証明ストアに格納されます。この資格証明ストアは、認証プロバイダのサーバーURLおよびユーザーと関連付けられた資格証明の格納に使用される、デバイス・ネイティブかつローカルなリポジトリです。実行時に、MAFでは、すべての資格証明がその使用前にIDM Mobile資格証明ストアに格納されているものとみなします。

connections.xmlファイルには、Webサービスの接続参照のadfCredentialStoreKey属性に、ログイン・サーバー接続のadfCredentialStoreKeyの値を指定して、ログイン・サーバーをWebサービスのセキュリティに関連付ける必要があります(次の2つの例を参照)。

注意:

JDeveloperではconnections.xmlファイルの概要エディタが提供されないため、「プロパティ」ウィンドウを使用して、<Reference>要素のadfcredentialStoreKey属性をログイン・サーバー接続のadfCredentialStoreKey属性用に構成されている名前で更新できます。または、「ソース」エディタを使用して属性を追加または更新できます。

次の例は、adfCredentialStoreKey="MyAuth"として参照されるWebサービス接続の定義を示しています。ここで、MyAuthとは、ログイン接続の参照名です。

<Reference name="URLConnection1"
           className="oracle.adf.model.connection.url.HttpURLConnection"
           adfCredentialStoreKey="MyAuth"
           xmlns="">
   <Factory className="oracle.adf.model.connection.url.URLConnectionFactory"/>
   <RefAddresses>
      <XmlRefAddr addrType="URLConnection1">
         <Contents>
            <urlconnection name="URLConnection1"
                           url="http://myhost.us.example.com:7777/
                                SecureRESTWebService1/Echo">
               <authentication style="challange">
                  <type>basic</type>
                  <realm>myrealm</realm>
               </authentication>
            </urlconnection>
         </Contents>
      </XmlRefAddr>
      <SecureRefAddr addrType="username"/>
      <SecureRefAddr addrType="password"/>
   </RefAddresses>
</Reference>

次の例は、ログイン接続の定義を示しています。ここでのMyAuthは、ログイン・サーバー接続における資格証明ストア・キーの値として使用されます。

<Reference name="MyAuthName"
           className="oracle.adf.model.connection.adfmf.LoginConnection"
           adfCredentialStoreKey="MyAuth"
           partial="false"
           manageInOracleEnterpriseManager="true"
           deployable="true"
           xmlns="">
   <Factory className="oracle.adf.model.connection.adfmf.LoginConnectionFactory"/>
   <RefAddresses>
      <XmlRefAddr addrType="adfmfLogin">
         <Contents>
            <login url="http://172.31.255.255:7777/
                        SecuredWeb1-ViewController-context-root/faces/view1.jsf"/>
            <logout url="http://172.31.255.255:7777/
                        SecuredWeb1-ViewController-context-root/faces/view1.jsf"/>
            <accessControl url="http://myhost.us.example.com:7777/
                           UserObjects/jersey/getUserObjects" />
            <idleTimeout value="10"/>
            <sessionTimeout value="36000"/>
            <userObjectFilter>
               <role name="testuser1_role1"/>
               <role name="testuser2_role1"/>
               <privilege name="testuser1_priv1"/>
               <privilege name="testuser2_priv1"/>
               <privilege name="testuser2_priv2"/>
            </userObjectFilter>
         </Contents>
      </XmlRefAddr>
   </RefAddresses>
</Reference>

認証の失敗によってWebサービス・リクエストが拒否された場合は、MAFにより適切な例外が戻され、適切なアクションが起動されます(「ロギングの使用方法と構成」を参照)。既存の例外で状況を適切に示せない場合は、新しい例外が追加されます。

「MAFアプリケーションで使用するエンド・ポイントの構成」で説明しているように、connections.xmlファイルは構成サービスの下でデプロイされ、管理されています。

FAR内のconnections.xmlファイルは、MAFアプリケーションのデプロイ時に集約されます。資格証明はデプロイメント固有のデータを表しており、FAR内に格納することは想定されていません。

Cookieの挿入に関する必知事項

MAFは、Cookieベースの認証のために、REST WebサービスのURLエンドポイントに関連付けられたログイン接続に対して、Cookieの挿入を実行します。

Cookieベースの認可のためにMAFアプリケーションがREST Webサービスを要求するたびに、MAFのセキュリティ・フレームワークは、REST Webサービスのトランスポート・レイヤーが、REST WebサービスのURLエンドポイントに関連付けられているログイン接続に対してCookieの挿入を実行できるようにします。これは実行時に処理され、MAF開発者によるMAFアプリケーションの構成は不要です。

ブラウザ・プロキシ情報の構成

企業のファイアウォールの外側に存在するWebサービスをコールするために、HTTPプロキシ・サーバーを使用するようにJavaのシステム・プロパティを構成します。このプロキシは、汎用接続フレームワークまたはjava.net APIのどちらかで定義できます。

デフォルトでは、MAFはアプリケーションのデプロイ先であるプラットフォームのシステム設定を使用してプロキシ情報を決定します。たとえば、iOSデバイスの設定ユーティリティを使用してプロキシ情報が設定されている場合、JVMは自動的にその情報を取り込みます。

注意:

MAFアプリケーションごとに異なるプロキシを定義できます。

デバイスの設定からプロキシ情報を取得しない場合は、最初に-Dcom.oracle.net.httpProxySourceシステム・プロパティを追加する必要があります。このプロパティのデフォルト値はnativeで、プロキシ情報をデバイスの設定から取得することを意味します。これを無効にするには、userなど別の値を指定します(例: -Dcom.oracle.net.httpProxySource=user)。

JVMは2つの異なるメカニズムを使用してネットワーク接続を有効にします。

  1. 汎用接続フレームワーク(GCF)。このメカニズムを使用する場合、プロキシはシステム・プロパティの-Dcom.sun.cdc.io.http.proxy=<host>:<port>によって定義されます。

  2. java.net API。このメカニズムを使用する場合、プロキシは標準のhttp.proxyHosthttp.proxyPortによって定義されます。

いずれの場合でも、maf.propertiesファイルに3つすべてのプロパティを定義することをお薦めしますが、そうすると次のようになります。

java.commandline.argument=-Dcom.oracle.net.httpProxySource=user
java.commandline.argument=-Dcom.sun.cdc.io.http.proxy=www-proxy.us.mycompany.com:80
java.commandline.argument=-Dhttp.proxyHost=www-proxy.us.mycompany.com
java.commandline.argument=-Dhttp.proxyPort=80

注意:

これらのプロパティはネットワーク・コールのJVM側のみに影響します。