26 RESTによるグリッド操作の実行

グリッド操作は、Coherence REST APIを使用して実行できます。Coherence REST APIでは、キャッシュとの相互作用に使用できる多数の操作を事前定義しています。さらに、必要に応じてアグリゲータやエントリ・プロセッサなどのカスタム操作を作成できます。

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

キーおよび値のタイプの指定

Coherence RESTサービスでは、公開されるキャッシュに関するメタデータが必要です。メタデータには、キャッシュ・エントリのキーと値のタイプ、およびキー・コンバータおよび値マーシャリング処理が含まれます。キーおよび値のタイプは、Coherenceが組込みのコンバータおよびマーシャリング処理を使用できるようにするために必要です(XMLとJSONがサポートされます)。

coherence-rest-config.xmlファイルを編集して、<key-class>および<value-class>要素を<resource>要素の中に含め、それぞれにキーと値のタイプを設定します。「resource」を参照してください。

ノート:

<key-class>および<value-class>要素は、キャッシュ構成ファイルの<cache-mapping>要素内または<resource>要素内のいずれかに定義できます。

次の例では、Stringキー・クラスおよびPersonユーザー・タイプの値クラスを定義します。

<resources>
   <resource>
      <cache-name>person</cache-name>
      <key-class>java.lang.String</key-class>
      <value-class>example.Person</value-class>
   </resource>
</resources>

単一オブジェクトのREST操作の実行

REST APIには、キャッシュ内の単一オブジェクト上での、GET、PUTおよびDELETE操作の実行のサポートが含まれます。

GET操作

GET http://host:port/cacheName/key

キーに基づいて、キャッシュから単一オブジェクトを返します。指定されたキーのオブジェクトが存在しない場合、404 (Not Found)のステータス・コードが返されます。get操作は部分的な結果をサポートします。「部分オブジェクトのREST操作の実行」を参照してください。オブジェクトにcom.tangosol.util.Versionsableインタフェースが実装されている場合、条件付きのGET操作がサポートされます。応答にバージョンが付加され、クライアントのオブジェクトが最新バージョンかどうか判定するために使用されます。クライアントにオブジェクトの最新バージョンがすでにある場合は、304 (Not Modified)のステータス・コードが返されます。

次のサンプル出力は、GET操作の応答を示しています。

* Client out-bound request
> GET http://127.0.0.1:8080/dist-http-example/1
> Accept: application/xml
 
* Client in-bound response
< 200
< Content-Length: 212
< Content-Type: application/xml
<
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Person><id>1</id><name>
Mark</name><address><street>500 Oracle Parkway</street><city>Redwood Shores</city>
<country>United States</country></address></Person>

* Client out-bound request
> GET http://127.0.0.1:8080/dist-http-example/1
> Accept: application/json

* Client in-bound response
< 200
< Content-Type: application/json
<
{"@type":"rest.Person","address":{"@type":"rest.Address","city":"Redwood Shores",
"country":"United States","street":"500 Oracle Parkway"},"id":1,"name":"Mark"}

PUT操作

PUT http://host:port/cacheName/key

キャッシュで単一オブジェクトを作成または更新します。オブジェクトが更新された場合は、200 (OK)のステータス・コードが返されます。オプティミスティック同時実行性チェックが失敗すると、409 (Conflict)のステータス・コードと、エンティティとして現在のオブジェクトが返されます。「並列処理制御の理解」を参照してください。

次のサンプル出力は、PUT操作の応答を示しています。

* Client out-bound request
> PUT http://127.0.0.1:8080/dist-test-sepx/1
> Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Person><id>1</id><name>
Mark</name><address><street>500 Oracle Parkway</street><city>Redwood Shores</city>
<country>United States</country></address></Person>
 
* Client in-bound response
< 200
< Content-Length: 0
<
 
* Client out-bound request
> PUT http://127.0.0.1:8080/dist-test-sepj/1
> Content-Type: application/json
{"@type":"rest.Person","id":1,"name":"Mark","address":{"@type":"rest.Address","str
eet":"500 Oracle Parkway","city":"Redwood Shores","country":"United States"}}
 
* Client in-bound response
< 200
< Content-Length: 0
<

Delete操作

DELETE http://host:port/cacheName/key

キーに基づいて、キャッシュから単一オブジェクトを削除します。オブジェクトが正常に削除された場合は200 (OK)のステータス・コードが返され、指定されたキーのオブジェクトが存在しない場合は、404 (Not Found)のステータス・コードが返されます。

複数オブジェクトのREST操作の実行

複数オブジェクトの操作により、ユーザーは単一のネットワーク・リクエストで複数のオブジェクトを取得または削除できるため、ネットワークの使用量を著しく削減でき、ネットワークのパフォーマンスが向上します。

ノート:

PUT操作は、大量の破損データが生成される場合があるので、サポートされていません。特には、エンティティ・ボディ内の(シリアライズされた形式の)個々のオブジェクトを、対応するキーと同じ順序でURLに配置されることが求められます。また、更新により置換を行うため、オブジェクト全体のシリアライズされた形式を提供する必要があり、さらにオーバーヘッドが発生する場合があります。

GET操作

GET http://host:port/cacheName/(key1, key2, ...)

指定されたキーに基づいて、キャッシュからオブジェクトのセットを返します。返されるオブジェクトの順序は定義されず、URLのキー順序と一致する必要もありません。欠落したオブジェクトは、警告なしで結果から省略されます。200 (OK)のステータス・コードが常に返されます。結果セットにオブジェクトがないと、空の結果セットが返されます。get操作は部分的な結果をサポートします。「部分オブジェクトのREST操作の実行」を参照してください。

DELETE操作

DELETE http://host:port/cacheName/(key1, key2, ...)

指定されたキーに基づいて、キャッシュから複数のオブジェクトを削除します。指定されたオブジェクトがキャッシュに存在しなくても、常に200 (OK)のステータス・コードが返されます。

部分オブジェクトのREST操作の実行

GET操作を実行するときに、取得するオブジェクト属性を指定できます。アプリケーションでオブジェクト全体を取得したくない(または取得する必要がない)場合があります。たとえば、アプリケーションでドロップダウンにオプションのリストを入力するために、場合によっては多数のプロパティを持つ巨大なオブジェクトから2つのプロパティしか必要ない場合があります。このような使用ケースをサポートするために、それぞれの読取り処理で、ユーザーが必要なオブジェクト・プロパティのリストをマトリックス・パラメータpとして受け取ります。

次の例では、個人のidおよびname属性のみを取得するget操作を実行します。

GET http://localhost:8080/people/123;p=id,name

住所のcountry属性も含める場合、リクエストURLは次のようになります。

GET http://localhost:8080/people/123;p=id,name,address:(country)

この方法では、URLに対応した単純な表記を使用して、アプリケーションで必要なプロパティのみを選択的に取得できます。

次のサンプル出力は、GET操作の応答を示しています。

* Client out-bound request
> GET http://127.0.0.1:8080/dist-test-sepj/1;p=name
> Accept: application/json
 
* Client in-bound response
< 200
< Transfer-Encoding: chunked
< Content-Type: application/json
<
{"name":"Mark"}

RESTによる問合せの実行

Coherence RESTによって、ユーザーはキャッシュへの問合せが可能です。CohQLがデフォルトの問合せ構文です。また、必要時に追加の問合せ構文を作成および使用できます。

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

直接問合せの使用

直接問合せは、REST URLでパラメータqの値として送信できる問合せ式です。デフォルトで、問合せ式はURLエンコードのCohQL式として指定する必要があります(CohQLの述語句)。『Oracle Coherenceでのアプリケーションの開発』エントリの結果セットでのフィルタリングに関する項を参照してください。直接問合せの構文は次のとおりです。

GET http://host:port/cacheName?q=query

たとえば、ageが18未満のPersonオブジェクトのpersonキャッシュを問い合せるとします。

GET http://host:port/person?q=age%3C18

デフォルトで直接問合せは無効です。直接問合せを有効にするには、coherence-rest-config.xmlファイルを編集し、問合せ先のリソースごとに<direct-query>要素を追加して、enabled属性をtrueに設定します。たとえば:

<resource>
   <cache-name>persons</cache-name>
   <key-class>java.lang.Integer</key-class>
   <value-class>example.Person</value-class>
   <direct-query enabled="true"/>
</resource>

問合せが、直接問合せに対応していないリソースで実行された場合、403 (Forbidden)の応答コードが返されます。

名前付き問合せの使用

名前付き問合せは、coherence-rest-config.xmlファイル内のリソースに対して構成する問合せ式です。デフォルトで、問合せ式はCohQL式として指定する必要があります(CohQLの述語句)。式をXMLファイル内で構成するため、特殊記号(<および>など)を対応するエンティティでエスケープする必要があります。『Oracle Coherenceでのアプリケーションの開発』エントリの結果セットでのフィルタリングに関する項を参照してください。さらに、名前付き問合せには必要に応じてコンテキスト値を含めることができます。名前付き問合せの構文は次のとおりです。

GET http://host:port/cacheName/namedQuery?param1=value1,param2=value2...

名前付き問合せを指定するには、任意の数の<query>要素を<resource>要素内に追加します。それぞれの要素には、問合せ式と名前バインディングがあります。「query」を参照してください。たとえば:

<resource>
   <cache-name>persons</cache-name>
   <key-class>java.lang.Integer</key-class>
   <value-class>example.Person</value-class>
   <query>
      <name>minors</name>
      <expression>age &lt; 18</expression>
   </query>
   <query>
      <name>first-name</name>
      <expression>name is :name</expression>
   </query>
</resource>

名前付き問合せを使用するには、REST URL内に問合せの名前を入力します。この例では、前述の例で定義されたminors名前付き問合せを使用しています。

GET http://host:port/persons/minors

パラメータには、コンテキスト値を問合せ式で置換えることができる柔軟性があります。次の例は、前述のfirst-name問合せ式で定義された:nameパラメータを使用して、nameプロパティがMarkのエントリのみを問い合せます。

http://host:port/persons/first-name?name=Mark

パラメータ名の前にはコロン記号を付ける必要があります(:paramName)。パラメータ・バインディングは型情報にアクセスできないため、比較演算子でTrueが予期される状況でFalseになる可能性があります。この動作を回避するために、問合せパラメータで型のヒントを指定します(:paramName;int)。表26-1は、サポートされる型のヒントを示しています。

表26-1 パラメータ型のヒント

ヒント

i、int

java.lang.Integer

s、short

java.lang.Short

l、long

java.lang.Long

f、float

java.lang.Float

d、double

java.lang.Double

I

java.math.BigInteger

D

java.math.BigDecimal

date

java.util.Date

uuid

com.tagosol.util.UUID

uid

com.tangosol.util.UID

package.MyClass

package.MyClass

名前付き問合せは、集計およびエントリ処理とともに使用することもできます。「RESTによる集計の実行」および「RESTによるエントリ処理の実行」をそれぞれ参照してください。たとえば:

http://host:port/persons/first-name?name=Mark/long-max(age)

http://host:port/persons/first-name?name=Mark/increment(age,1)

問合せのソート順の指定

sortマトリックス・パラメータは、REST URL内で使用されるオプションのパラメータです。これにより問合せから返された結果の順序を指定できます。sortパラメータは、直接問合せと名前付け問合せの両方で使用できます。sortパラメータの値は、ソートするプロパティのカンマ区切りリストを表し、それぞれにソート順を決定するオプションの:asc (デフォルト)または:desc修飾子を指定できます。たとえば、個人データのリストを名前の姓でソートし、家族のメンバーは年齢の大きい順にソートする場合、sortパラメータは次のように定義します。

GET http://host:port/persons/minors;sort=lastName,age:desc

次の例は、直接問合せ内でsortパラメータを使用します。

GET http://host:port/persons;sort=lastName,age:desc?q=age%3C18

問合せ結果サイズの制限

大きなキャッシュに対する問合せは、メモリー不足エラーが発生する可能性のある大きな結果セットを返す可能性があります。大きなキャッシュを問合せる場合は、問合せでキーの使用がオプションである場合でも、常にキーを使用するようにしてください。キーが省略されると、問合せですべてのキャッシュ・エントリが返される場合があります。

クライアントに返される結果の数を制限する方法は、startおよびcountマトリックス・パラメータを使用する方法と、max-results属性を使用する方法の2つがあります。いずれの方法も、直接および名前付け問合せでサポートされます。

startおよびcountパラメータは、返す結果のサブセットを決定するオプションの整数の引数です。次の例は、名前付け問合せでパラメータを使用し、名前によってソートされた最初の10のエントリを返します。

http://host:port/persons/minors;start=0;count=10;order=name:asc

次の例は、直接問合せ内でパラメータを使用しています。

GET http://host:port/persons;start=0;count=10?q=age%3C18

max-results属性はcoherence-rest-config.xmlファイル内で使用され、クライアントに返される結果の数を明示的に制限します。この属性では、キャッシュから返されるエントリの数が制限されないことに注意してください。次の例では、max-results属性を設定します。

<resource max-results="50">
   <cache-name>persons</cache-name>
   <key-class>java.lang.Integer</key-class>
   <value-class>example.Person</value-class>
   <direct-query enabled="true" max-results="25">
   <query max-results="25">
      <name>minors</name>
      <expression>age &lt; 18</expression>
   </query>
</resource>

両方が指定された場合、直接または名前付け問合せに対するmax-results値は、リソースのmax-results値を無視します。問合せがcountパラメータを含んでおり、max-results要素も指定されている場合、低い値の方が使用されます。

キーのみの取出し

キャッシュに格納されたエントリのキーのみを検索することは可能です。キーの操作ではページングとソートはサポートされません。したがって、それらの問合せパラメータが送信された場合、無視されます。次のキーの取出し操作がサポートされています。

GET http://host:port/cacheName/keys

キャッシュ中のすべてのエントリのキーを返します。

GET http://host:port/cacheName/keys?q=query

直接問合せの基準を満たす、すべてのエントリのキーを返します。

GET http://host:port/cacheName/namedQuery/keys

名前付け問合せの基準を満たす、すべてのエントリのキーを返します。

カスタム問合せエンジンの使用

問合せエンジンは、直接と名前付け問合せの両方に対して問合せを実行します。デフォルト問合せエンジンは、CohQL構文(CohQLの述語部分)を使用して表現された問合せを実行します。

ノート:

Coherence RESTのデフォルトの問合せエンジンでは、MvelExtractorが使用されます。デフォルトの問合せエンジンを使用するCoherence RESTユーザーは、MvelExtractorを使用してキャッシュ索引を作成する必要があります。
カスタム問合せエンジンの実装によって、異なる問合せ表現構文を使用することや、Coherence以外のデータ・ソースに問合せを実行できます(たとえば、キャッシュ内にないエントリについてデータベースに問合せを実行するなど)。

この項には次のトピックが含まれます:

カスタム問合せエンジンの実装

カスタム問合せエンジンには、com.tangosol.coherence.rest.query.QueryEnginecom.tangosol.coherence.rest.query.Queryインタフェースを実装する必要があります。カスタム実装は、com.tangosol.coherence.rest.query.AbstractQueryEngineベース・クラスも拡張します。これにより、問合せ式の分析やパラメータ・バインドの処理を実行する有用なメソッドが提供されます。ベース・クラスでは、実行時のパラメータ置換と、問合せパラメータ値の一部として送信される型のヒントもサポートされます。パラメータ名および型のヒントはCohQL使用に従い、他の問合せエンジンの実装に使用できます。「名前付き問合せの使用」を参照してください。

次の例は、データベースに対してSQL問合せを直接実行し、キャッシュ・リードスルーを強制する簡単問合せエンジンの実装です。実際には、問合せエンジンの実装でランタイム・パラメータ・バインディングがサポートされる場合が多いでますが、この例にはありません。

public class SqlQueryEngine
   extends AbstractQueryEngine
   {
   protected Connection m_con;
   private static final String DB_DRIVER = "oracle.jdbc.OracleDriver";
   private static final String DB_URL = "jdbc:oracle:thin:@localhost:1521:orcl";
   private static final String DB_USERNAME = "username";
   private static final String DB_PASSWORD = "password";
 
   public SqlQueryEngine()
      {
      configureConnection();
      }
 
   @Override
   public Query prepareQuery(String sQuery, Map<String, Object> mapParams)
      {
      ParsedQuery parsedQuery = parseQueryString(sQuery);
      String  sSQL            = createSelectPKQuery(parsedQuery.getQuery());
      return new SqlQuery(sSQL);
      }
 
   protected void configureConnection()
      {
      try
         {
         Class.forName(DB_DRIVER);
         m_con = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);
         m_con.setAutoCommit(true);
         }
      catch (Exception e)
         {
         throw new RuntimeException(e);
         }
      }
 
   protected String createSelectPKQuery(String sSQL)
      {
      return "SELECT id,name,age FROM " +
         sSQL.substring(sSQL.toUpperCase().indexOf("FROM") + 4);
      }
 
   private class SqlQuery
      implements Query
      {
      protected String m_sql;

      public SqlQuery(String sql)
         {
         m_sql = sql;
         }
 
      @Override
      public Collection values(NamedCache cache, String sOrder, int nStart, 
         int cResults)
         {
         // force read through
         Set setKeys = keySet(cache);
         return cache.getAll(setKeys).values();
         }
 
      @Override
      public Set keySet(NamedCache cache)
         {
         Set setKeys = new HashSet();
         try
            {
            PreparedStatement stmt   = m_con.prepareStatement(m_sql);
            ResultSet         result = stmt.executeQuery();
            while (result.next())
               {
               Object oKey = result.getLong(1);
               setKeys.add(oKey);
               Person person = new Person(result.getString("name"),
                  result.getInt("age"));
               cache.put(oKey, person);
               }
               stmt.close();
            }
         catch (SQLException e)
            {
            throw new RuntimeException(e);
            }
            return setKeys;
         }
   }
}
カスタム問合せエンジンの有効化

カスタム問合せエンジンはcoherence-rest-config.xmlファイルで有効化します。カスタム問合せエンジンを有効にするには、最初に<query-engines>要素内に<engine>を追加して実装を登録します。この要素には、問合せエンジンの名前と、実装クラスの完全修飾名が含まれています。「engine」を参照してください。たとえば:

<query-engines>
   <engine>
      <name>SQL-ENGINE</name>
      <class-name>package.SqlQueryEngine</class-name>
   </engine>
</query-engines>

名前付け問合せまたは直接問合せのためのカスタム問合せエンジンを明示的に指定するには、<direct-query>要素または<query>要素内に、カスタム問合せエンジンの登録名を参照するengine属性を追加します。たとえば:

<resource>
   <cache-name>persons</cache-name>
   <key-class>java.lang.Integer</key-class>
   <value-class>example.Person</value-class>
   <query engine="SQL-ENGINE">
      <name>less-than-1000</name>
      <expression>select * from PERSONS where id &lt; 1000</expression>
   </query>
   <direct-query enabled="true" engine="SQL-ENGINE"/>
 </resource>

カスタム問合せエンジンをデフォルトの問合せエンジンにするには、登録済の名前としてDEFAULT(大文字が必須)を使用してください。次の定義はデフォルトCohQLベースの問合せエンジンをオーバーライドし、engine属性を指定していない場合には自動的に使用されます。

<query-engines>
   <engine>
      <name>DEFAULT</name>
      <class-name>package.SqlQueryEngine</class-name>
   </engine>
</query-engines>

RESTによる集計の実行

キャッシュ内のデータの集計を実行できます。Coherence RESTには事前定義のアグリゲータのセットが含まれ、必要に応じてカスタムのアグリゲータも作成できます。

この項には次のトピックが含まれます:

RESTの集計の構文

次の例は、RESTを使用して集計を実行する方法を示します。集計に成功すると、200 (OK)のステータス・コードがエンティティとして集約の結果とともに返されます。

  • キャッシュ内のすべてのエントリを集計します。

    GET http://host:port/cacheName/aggregator(args, ...)
    
  • 問合せ結果を集計します。問合せはURLエンコードのCohQL式として指定する必要があります(CohQLの述語句)。

    GET http://host:port/cacheName/aggregator(args, ...)?q=query
    
    GET http://host:port/cacheName/namedQuery/aggregator(args, ...)?param1=value1
    
  • 指定されたエントリを集計します。

    GET http://host:port/cacheName/(key1, key2, ...)/aggregator(args, ...)
    

Coherence RESTは、(アグリゲータ関連のURLセグメントから)アグリゲータを作成するための簡単な戦略を提供します。初期状態のCoherence RESTでは、単一のタイプcom.tangosol.util.ValueExtractor (LongMaxDoubleMaxなど)のパラメータを受け入れるコンストラクタにより、任意の登録された(組込みまたはユーザー登録の)アグリゲータを解決できます。URL内のアグリゲータのコールにパラメータが含まれない場合、com.tangosol.util.extractor.IdentityExtractorを使用してアグリゲータが作成されます。

URL内のアグリゲータ・セグメントにパラメータが含まれない場合、または単一のValueExtractorを受け入れるコンストラクタが存在しない場合、Coherence RESTはデフォルトのコンストラクタを使用してアグリゲータのインスタンス化を試行し、これは、一部の組込みのアグリゲータ(countなど)に最適な動作です。

次の例では、キャッシュ内で最も高齢な人を取得します。

GET http://host:port/people/long-max(age)

次の例では、数字のみを含むキャッシュで最大数を計算します。

GET http://host:port/numbers/comparable-max()

次の例では、個人データのキャッシュのサイズを計算します。

GET http://host:port/people/count()

事前定義のアグリゲータのリスト

次の事前定義のアグリゲータがサポートされます。

アグリゲータ名 アグリゲータ

big-decimal-average

BigDecimalAverage.class

big-decimal-max

BigDecimalMax.class

big-decimal-min

BigDecimalMin.class

big-decimal-sum

BigDecimalSum.class

double-average

DoubleAverage.class

double-max

DoubleMax.class

double-min

DoubleMin.class

double-sum

DoubleSum.class

long-max

LongMax.class

long-min

LongMin.class

long-sum

LongSum.class

comparable-max

ComparableMax.class

comparable-min

ComparableMin.class

distinct-values

DistinctValues.class

count

Count.class

カスタム・アグリゲータの作成

カスタム・アグリゲータ・タイプを定義するには、REST URLで使用する名前と、com.tangosol.util.InvocableMap.EntryAggregatorインタフェースまたはcom.tangosol.coherence.rest.util.aggregator.AggregatorFactoryインタフェースを実装するクラスを指定します。

(ほとんどの事前定義のアグリゲータのように)集計が単一のプロパティまたはキャッシュ値自身で実行される場合、EntryAggregator実装は単純なシナリオで使用されます。

より複雑な戦略が必要な場合、AggregatorFactoryインタフェースが使用されます。この実装で、アグリゲータ・パラメータを含むURLセグメントを解決し、それらのパラメータを使用して適切なアグリゲータを作成できる必要があります。

カスタム・アグリゲータは、coherence-rest-config.xmlファイルの<aggregators>要素で構成されます。「aggregator」を参照してください。次の例では、EntryAggregatorのカスタム実装とAggregatorFactoryのカスタム実装を構成しています。

<aggregators>
   <aggregator>
      <name>my-simple-aggr</name>
      <class-name>com.foo.MySimpleAggregator</class-name>
   </aggregator>
   <aggregator>
      <name>my-complex-aggr</name>
      <class-name>com.foo.MyAggreagatorFactory</class-name>
   </aggregator>
</aggregators>

RESTによるエントリ処理の実行

キャッシュ内の1つ以上のオブジェクト上でエントリ・プロセッサを呼び出せます。Coherence RESTには事前定義のエントリ・プロセッサのセットが含まれ、必要に応じてカスタムのエントリ・プロセッサも作成できます。

この項には次のトピックが含まれます:

RESTのエントリ・プロセッサの構文

次の例は、RESTを使用してエントリ処理を実行する方法を示します。処理に成功すると、200 (OK)のステータス・コードがエンティティとして処理の結果とともに返されます。

  • キャッシュ内のすべてのエントリを処理します。

    POST http://host:port/cacheName/processor(args, ...)
    
  • 問合せ結果を処理します。

    POST http://host:port/cacheName/processor(args, ...)?q=query
    
    POST http://host:port/cacheName/namedQuery?param1=value1/processor(args, ...)
    
  • 指定されたエントリを処理します。

    POST http://host:port/cacheName/(key1, key2, ...)/processor (args, ...)
    

アグリゲータと異なり、プロセッサ(事前定義のプロセッサも含む)には、多彩な作成パターンがあるため、Coherence RESTではプロセッサの作成について何の想定もしていません。そのかわり、それぞれのエントリ・プロセッサ実装に対して、com.tangosol.coherence.rest.util.processor.ProcessorFactoryインタフェースの実装が存在する必要があります。これにより、URLセクションからの文字列の入力を処理して、プロセッサ・インスタンスをインスタンス化できます。Coherence RESTは、初期状態ではNumberIncrementorおよびNumberMultiplierの2つのファクトリを備えています。

次の例では、キャッシュ内の各個人の年齢を5つ増加させます。

POST http://localhost:8080/people/increment(age, 5)

次の例では、数字のみを含むキャッシュ内の各数字を10倍します。

POST http://localhost:8080/numbers/multiply(10)

事前定義のエントリ・プロセッサのリスト

次の事前定義のプロセッサがサポートされます。

プロセッサ名 プロセッサ

increment

常に新しい(増加された)値を返すNumberIncrementorインスタンス

post-increment

常に古い(増加されない)値を返すNumberIncrementorインスタンス

multiply

常に新しい(乗算された)値を返すNumberMultiplierインスタンス

post-multiply

常に古い(乗算されない)値を返すNumberMultiplierインスタンス

カスタム・エントリ・プロセッサの作成

カスタム・エントリ・プロセッサを定義するには、REST URLで使用される名前とcom.tangosol.coherence.rest.util.processor.ProcessorFactoryインタフェースを実装するクラスを指定します。

カスタム・エントリ・プロセッサは、coherence-rest-config.xmlファイルの<processors>要素内で構成されます。「processors」を参照してください。次の例では、ProcesorFactoryのカスタム実装を構成します。

<processors>
   <processor>
      <name>my-processor</name>
      <class-name>com.foo.MyProcessorFactory</class-name>
   </processor>
</processors>

並列処理制御の理解

Coherence RESTでは、HTTPプロトコルに明確にマップされる場合のみ、オプティミスティック同時実行性をサポートします。アプリケーションがcom.tangosol.util.Versionableを実装するオブジェクトのGETリクエストを送信する場合、現在のバージョン識別子がHTTP ETagで返されます(オブジェクトの表現と同様に、バージョン識別子がJSON/XMLのシリアライズ形式に含まれると仮定されます)。アプリケーションが同じGETリクエストをリソースに送信するとき、今回はIf-None-Matchヘッダーに同じETag値を使用する場合、Coherence RESTは、アプリケーションにはリソースの最新のバージョンがあることを示す304のステータスを返します。

同様に、アプリケーションがPUTリクエストを送信してオブジェクトを更新する際に、キャッシュ・オブジェクトでcom.tangosol.util.Versionableインタフェースを実装していると、Coherence RESTは既存オブジェクトのバージョンと新規オブジェクトのバージョンが一致する場合のみ更新を実行します。それ以外の場合は、クライアントが変更を再度適用して再試行できるように、現在のオブジェクトとともに409 Conflictステータスが返されます。

次の例は、これらの概念について説明しています。

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import javax.ws.rs.core.MediaType;
import org.codehaus.jettison.json.JSONObject;

public class ConcurrencyTests
   {
   public static void main(String[] asArg)
      throws Exception
      {
      Client      client      = Client.create();
      String      url         = "http://localhost:" + getPort() + "/dist-test1/2";
      WebResource webResource = client.resource(url);
 
      // perform a GET of a server-side resource that implements Versionable
      ClientResponse response = webResource
         .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
      assert 200 == response.getStatus(); /* OK */
 
      // verify that the current version of the resource is 1
      JSONObject json    = new JSONObject(response.getEntity(String.class));
      String     version = json.getString("versionIndicator");
      assert "1".equals(version);
      assert new EntityTag("1").equals(response.getEntityTag());
 
      // perform a conditional GET of the same resource and verify that we
      // get a response status of 304: Not Modified
      response = webResource
         .accept(MediaType.APPLICATION_JSON)
         .header ("If-None-Match", '"' + version + '"').get(ClientResponse.class);
      assert 304 == response.getStatus(); /* Not Modified */
 
      // simulate a version change on the server-side by rolling back the
      // version indicator on our representation of the resource
      json.put("versionIndicator", String.valueOf(0));
 
      // perform a conditional PUT of the same resource and verify that we
      // get a response status of 409: Conflict
      response = webResource
         .accept(MediaType.APPLICATION_JSON)
         .put(ClientResponse.class, json);
      assert 409 == response.getStatus(); /* Conflict */
 
      // retry again with the returned value and verify that we now get a
      // response status of 200: OK
      json = new JSONObject(response.getEntity(String.class));
      response = webResource
         .accept(MediaType.APPLICATION_JSON)
         .put(ClientResponse.class, json);
      assert 200 == response.getStatus(); /* OK */
      }
   }

キャッシュの別名の指定

キャッシュの別名は、単純化されたキャッシュ名を指定するために使用されます。別名は、キャッシュ名がREST URLパス・セグメントに最適でない場合に使用されます。単純化された名前は、実際のキャッシュ名にマップされます。

キャッシュの別名を定義するには、coherence-rest-config.xmlファイルを編集して、<name>要素を<resource>要素内に含め、その値を単純化されたキャッシュ名に設定します。

次の例では、dist-extend-not-ideal-name-for-a-cache*という名前のキャッシュに、peopleという名前のキャッシュの別名を作成します。

<resources>
   <resource name="people">
      <cache-name>dist-extend-not-ideal-name-for-a-cache*</cache-name>
      ...
   </resource>
</resources>

サーバー送信イベントの使用

サーバー送信イベントでは、Coherence RESTアプリケーションでキャッシュ・イベントをCoherenceクラスタから自動的に受信できます。たとえば、キャッシュ・エントリの挿入または削除時にイベントを受信できます。サーバー送信イベントの使用例の詳細は、『Oracle Coherenceのインストール』Coherence RESTの例に関する項のCoherence RESTの例を参照してください。

サーバー送信イベントでは、Grizzly HTTPサーバーまたはJetty HTTPサーバーのいずれかを使用する必要があります。「Grizzly HTTPサーバーの使用」および「Jetty HTTPサーバーの使用」をそれぞれ参照してください。また、サーバー送信イベントはWebブラウザでサポートされている必要があります。サポートの詳細は、ブラウザのドキュメントを参照してください。

この項には次のトピックが含まれます:

サーバー送信イベントの受信

Webページは、EventSourceオブジェクトを使用してサーバー送信イベントを受信します。EventSourceオブジェクトは、指定したURI に接続します(イベントが生成され、カスタムEventListenersを追加して、サーバー送信の受信イベントがリスニングおよび処理されます)。Coherence RESTの例の次のコードでは、/cache/contacts URIをリスニングしてinsertupdatedeleteおよびerrorイベントのイベント・リスナーを追加する新しいEventSourceオブジェクトを、JavaScriptを使用して作成します。

$scope.startListeningContacts = function() {
   $scope.contacts.listening = true;
   $scope.contacts.started   = true;

   if ($scope.contacts.filter == 'all') {
      query = '';
   }
   else if ($scope.contacts.filter == '>=45') {
      query = '?q=age%20>=%2045';
      $scope.contacts.filter = 'age >= 45';
   }
   else {
      query = '?q=age%20<%2045';
      $scope.contacts.filter = 'age < 45';
   }

   $scope.contacts.status  = 'Listening: ' + $scope.contacts.filter;
   var eventSourceContacts = new EventSource('/cache/contacts' + query);

   eventSourceContacts.addEventListener('insert', function(event) {
      $scope.contacts.insertCount++;
      $scope.contacts.allCount++;
      $scope.updateContactEvent(JSON.parse(event.data), 'insert');
      $scope.$apply();
   });

   eventSourceContacts.addEventListener('update', function(event) {
      $scope.contacts.updateCount++;
      $scope.contacts.allCount++;
      $scope.updateContactEvent(JSON.parse(event.data), 'update');
      $scope.$apply();
   });

   eventSourceContacts.addEventListener('delete', function(event) {
      $scope.contacts.deleteCount++;
      $scope.contacts.allCount++;
      $scope.updateContactEvent(JSON.parse(event.data), 'delete');
      $scope.$apply();
   });

   eventSourceContacts.addEventListener('error', function(event) {
      var eventData = JSON.parse(event.data);
      alert('error');
   });
};

イベントが受信されると、イベントに基づいて、意味のあるアクションをアプリケーションで選択できます。たとえば:

$scope.updateContactEvent = function(eventData, eventType) {
   $scope.contacts.eventType = eventType;
   $scope.contacts.eventKey  = eventData.key.firstName + ' ' + 
      eventData.key.lastName;

   $scope.contacts.eventNewValue = 'N/A';
   $scope.contacts.eventOldValue = 'N/A';

   if (eventType == 'insert' || eventType == 'update') {
      $scope.contacts.eventNewValue = $scope.getContactString(eventData.newValue);
   }
   if (eventType == 'delete' || eventType == 'update') {
      $scope.contacts.eventOldValue = $scope.getContactString(eventData.oldValue);
   }
};