この章では、テキスト・ファイルから読み取ったドメイン・オブジェクトをCoherenceキャッシュに移入する方法を学習します。
この章では次の項について説明します。
これまで、オブジェクトをキャッシュに格納したりキャッシュから取得したりする場合には、個別に実行してきました。この方法では、特にパーティション化されたりレプリケートされたりしたキャッシュで、putメソッドをコールするたびにネットワーク・トラフィックが増加することがあります。また、putメソッドをコールするたびに、キャッシュ内で置き換えられたばかりのオブジェクトが返され、不要なオーバーヘッドがさらに増えます。putAllメソッドを使用すると、キャッシュのロードを大幅に効率化できます。
この章に記載されているタスクを実行するには、まず第4章「複合オブジェクトの操作」で説明するプロジェクトを完了する必要があります。また、テキスト・ファイルを読み取るにはjava.io.BufferedReader、テキスト・ファイルを解析するにはjava.lang.String.splitメソッド、日付を解析するにはjava.text.SimpleDateFormatについて十分理解している必要があります。
この演習では、Coherenceキャッシュにドメイン・オブジェクトを移入するコンソール・アプリケーションを作成する方法を示します。このアプリケーションはCoherenceのcom.tangosol.io.pof.PortableObject実装を使用して、Portable Object format (POF)にオブジェクトをシリアライズします。
この演習では、Contactオブジェクトを取得できるキー、キャッシュにデータを提供するジェネレータおよびキャッシュをロードするローダーを作成します。
ドメイン・オブジェクトのキーを持つクラスを作成するには:
Loadingという名前の新規アプリケーション・クライアント・プロジェクトを作成します。「Configuration」ドロップダウン・リストから「CoherenceConfig」を選択します。「New Application Client Project」ウィザードの「Application Client Module」ページで、「Create a default Main class」チェック・ボックスの選択を解除します。
新規プロジェクトの作成の詳細は、「複合オブジェクトの作成とキャッシュ」を参照してください。
前の演習(Contacts)で作成したAddressクラス、PhoneNumberクラスおよびContactクラスに関連するクラスとファイルを追加します。これらのファイルは、c:\home\oracle\workspace\Contacts\appClientModuleディレクトリおよびc:\home\oracle\workspace\Contacts\build\classesディレクトリにあります。
「Project Explorer」でLoadingプロジェクトを右クリックして、「Properties」を選択します。「Properties for Loading」ダイアログ・ボックスで、「Java Build Path」を選択します。「Projects」タブで、「Add」をクリックします。図 5-1に示されているように、「Required Project Selection」ダイアログ・ボックスから「Contacts」プロジェクトを選択します。
「Order and Export」タブで、「Up」ボタンと「Down」ボタンを使用して「Contacts」をリストの先頭に移動します。タブの内容は図5-2のようになります。
情報追跡の対象となる従業員のキーを提供するContactIDクラスを作成します。Javaクラスの作成の詳細は、「Javaクラスの作成」を参照してください。
従業員の姓名に基づいた連絡先IDを作成します。このオブジェクトは、Contactオブジェクトを取得するキーとして機能します。
このクラスはPOFシリアライズを使用するので、PortableObjectインタフェース、writeExternalメソッドとreadExternal PortableObjectメソッドのほか、equalsオブジェクト、hashCodeオブジェクトおよびtoStringオブジェクトのメソッドを実装する必要があります。
| 注意:キャッシュのキーと値は、シリアライズ可能(たとえば、 java.io.Serializable)である必要があります。また、キャッシュ・キーはhashCodeメソッドとequalsメソッドの実装を提供する必要があり、これらのメソッドはクラスタ・ノード間で一貫性のある結果を返す必要があります。つまり、hashCodeオブジェクト・メソッドとequalsオブジェクト・メソッドの実装はオブジェクトのシリアライズ可能な状態(オブジェクトの一時的でないフィールド)にのみ基づいている必要があります。String、Integer、Dateなどの大半の組込みJavaタイプはこの要件を満たしています。一部のキャッシュの実装(特にパーティション・キャッシュ)では、等価の検証にキー・オブジェクトのシリアライズされた形式を使用します。つまり、equalsメソッドによりtrueが返されるキーは、同様の方法でシリアライズされる必要があります。ほとんどの組込みのJavaタイプはこの要件を満たしています。 | 
例5-1は、考えられるContactIdクラスの実装を示しています。
例5-1 単純な連絡先IDクラス
package com.oracle.handson;
import com.tangosol.io.pof.PofReader;
import com.tangosol.io.pof.PofWriter;
import com.tangosol.io.pof.PortableObject;
 
import com.tangosol.util.Base;
import com.tangosol.util.HashHelper;
 
import java.io.IOException;
 
/**
* ContactId is a key to the person for whom information is
* tracked.
*/
public class ContactId implements PortableObject
    {
    // ----- constructors ---------------------------------------------------
 
    /**
    * Default constructor (necessary for PortableObject implementation).
    */
    public ContactId() 
        {
        }
 
    /**
    * Construct a contact person.
    *
    */
    public ContactId(String FirstName, String LastName)
        {
        super();
        this.FirstName = FirstName;
        this.LastName  = LastName;
        }
 
    // ----- accessors ------------------------------------------------------
 
    /**
    * Return the first name.
    *
    */
    public String getFirstName()
        {
        return FirstName;
        }
 
    /**
    * Return the last name.
    *
    */
    public String getLastName()
        {
        return LastName;
        }
 
    // ----- PortableObject interface ---------------------------------------
 
    public void readExternal(PofReader reader)
            throws IOException
        {
        FirstName = reader.readString(0);
        LastName = reader.readString(1);
        }
 
    public void writeExternal(PofWriter writer)
            throws IOException
        {
        writer.writeString(0, FirstName);
        writer.writeString(1, LastName);
        }
 
    // ----- Object methods -------------------------------------------------
 
    public boolean equals(Object oThat)
        {
        if (this == oThat)
            {
            return true;
            }
        if (oThat == null)
            {
            return false;
            }
 
        ContactId that = (ContactId) oThat;
        return Base.equals(getFirstName(), that.getFirstName()) &&
               Base.equals(getLastName(),  that.getLastName());
        }
 
    public int hashCode()
        {
        return HashHelper.hash(getFirstName(),
               HashHelper.hash(getLastName(), 0));
        }
 
    public String toString()
        {
        return getFirstName() + " " + getLastName();
        }
 
    // ----- data members ---------------------------------------------------
 
    /**
    * First name.
    */
    private String FirstName;
 
    /**
    * Last name.
    */
    private String LastName;
    }
POF構成ファイルを編集します。ContactIdクラスの<user-type>エントリをcontacts-pof-config.xmlファイルに追加します。ファイルは例5-2のようになります。
例5-2 ContactIdエントリを持つPOF構成ファイル
<?xml version="1.0"?>
 
<pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"
              xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config coherence-pof-config.xsd">
  <user-type-list>
 
    <!-- coherence POF user types -->
    <include>coherence-pof-config.xml</include>
 
    <!-- com.tangosol.examples package -->
    <user-type>
      <type-id>1001</type-id>
      <class-name>com.oracle.handson.Contact</class-name>
    </user-type>
    <user-type>
      <type-id>1002</type-id>
      <class-name>com.oracle.handson.Address</class-name>
    </user-type>
    <user-type>
        <type-id>1003</type-id>
      <class-name>com.oracle.handson.PhoneNumber</class-name>
    </user-type>
    <user-type> 
     <type-id>1004</type-id>
      <class-name>com.oracle.handson.ContactId</class-name>
    </user-type>  
   </user-type-list>
  <allow-interfaces>true</allow-interfaces>
  <allow-subclasses>true</allow-subclasses>
</pof-config>
ランダムな従業員の連絡先の名前やアドレスを生成するDataGeneratorという名前のJavaクラスを作成します。詳細は、「Javaクラスの作成」を参照してください。
前の演習で作成したAddressクラス、PhoneNumberクラスおよびContactクラスを使用します。ランダムな名前、アドレス、電話番号および年齢を生成するには、java.util.Randomを使用します。
例5-3は、考えられるデータ生成の実装を示しています。この実装では、従業員の連絡先情報が記載されたテキスト・ファイル(contacts.cvs)を作成します。
例5-3 データ生成クラスのサンプル
package com.oracle.handson;
 
import com.oracle.handson.Address;
import com.oracle.handson.Contact;
import com.oracle.handson.PhoneNumber;
import com.tangosol.util.Base;
 
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
 
import java.sql.Date;
 
import java.util.Collections;
import java.util.Random;
 
 
/**
* DataGenerator is a generator of sample contacts.
*/
public class DataGenerator
    {
    // ----- static methods -------------------------------------------------
 
    /**
    * Generate contacts.
    */
    public static void main(String[] asArg)
            throws IOException
        {
        String       sFile = asArg.length > 0 ? asArg[0] : FILENAME;
        int          cCon  = asArg.length > 1 ? Integer.parseInt(asArg[1]) : 1000;
        OutputStream out   = new FileOutputStream(sFile);
 
        generate(out, cCon);
        out.close();
        }
 
    /**
    * Generate the contacts and write them to a file.
    */
    public static void generate(OutputStream out, int cContacts)
            throws IOException
        {
        PrintWriter writer = new PrintWriter(new BufferedWriter(
                new OutputStreamWriter(out)));
 
        for (int i = 0; i < cContacts; ++i)
            {
            StringBuffer sb = new StringBuffer(256);
 
            //contact person
            sb.append("John,")
              .append(getRandomName())
              .append(',');
 
            // home and work addresses
            sb.append(Integer.toString(Base.getRandom().nextInt(999)))
              .append(" Beacon St.,,") /*street1,empty street2*/
              .append(getRandomName()) /*random city name*/
              .append(',')
              .append(getRandomState())
              .append(',')
              .append(getRandomZip())
              .append(",US,Yoyodyne Propulsion Systems,")
              .append("330 Lectroid Rd.,Grover's Mill,")
              .append(getRandomState())
              .append(',')
              .append(getRandomZip())
              .append(",US,");
 
            // home and work telephone numbers
            sb.append("home,")
              .append(Base.toDelimitedString(getRandomPhoneDigits(), ","))
              .append(",work,")
              .append(Base.toDelimitedString(getRandomPhoneDigits(), ","))
              .append(',');
 
            // random birth date in millis before or after the epoch
            sb.append(getRandomDateInMillis());
 
            writer.println(sb);
            }
        writer.flush();
        }
 
    /**
    * Return a random name.
    *
    */
    private static String getRandomName()
        {
        Random rand = Base.getRandom();
        int    cCh  = 4 + rand.nextInt(7);
        char[] ach  = new char[cCh];
 
        ach[0] = (char) ('A' + rand.nextInt(26));
        for (int of = 1; of < cCh; ++of)
            {
            ach[of] = (char) ('a' + rand.nextInt(26));
            }
        return new String(ach);
        }
 
    /**
    * Return a random phone muber.
    * The phone number includes access, country, area code, and local
    * number.
    *
    */
    private static int[] getRandomPhoneDigits()
        {
        Random rand = Base.getRandom();
        return new int[] 
            {
            11,                   // access code
            rand.nextInt(99),     // country code
            rand.nextInt(999),    // area code
            rand.nextInt(9999999) // local number
            };
        }
 
    /**
    * Return a random Phone.
    *
    */
    private static PhoneNumber getRandomPhone()
        {
        int[] anPhone = getRandomPhoneDigits();
 
        return new PhoneNumber((short)anPhone[0], (short)anPhone[1],
                (short)anPhone[2], anPhone[3]);
 
        }
    /**
    * Return a random Zip code.
    *
    */
    private static String getRandomZip()
        {
        return Base.toDecString(Base.getRandom().nextInt(99999), 5);
        }
 
    /**
    * Return a random state.
    *
    */
    private static String getRandomState()
        {
        return STATE_CODES[Base.getRandom().nextInt(STATE_CODES.length)];
        }
 
    /**
    * Return a random date in millis before or after the epoch.
    *
    */
    private static long getRandomDateInMillis()
        {
        return (Base.getRandom().nextInt(40) - 20) * Contact.MILLIS_IN_YEAR;
        }
 
    /**
    * Generate a Contact with random information.
    *
    */
    public static Contact getRandomContact()
        {
        return new Contact("John",
                getRandomName(),
                new Address("1500 Boylston St.", null, getRandomName(),
                    getRandomState(), getRandomZip(), "US"),
                new Address("8 Yawkey Way", null, getRandomName(),
                    getRandomState(), getRandomZip(), "US"),
                Collections.singletonMap("work", getRandomPhone()),
                new Date(getRandomDateInMillis()));
        }
 
    // ----- constants ------------------------------------------------------
 
    /**
    * US Postal Service two letter postal codes.
    */
    private static final String[] STATE_CODES = 
        {
            "AL", "AK", "AS", "AZ", "AR", "CA", "CO", "CT", "DE", "OF", "DC",
            "FM", "FL", "GA", "GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY",
            "LA", "ME", "MH", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE",
            "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "MP", "OH", "OK", "OR",
            "PW", "PA", "PR", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VI",
            "VA", "WA", "WV", "WI", "WY"
        };
    /**
    * Default contacts file name.
    */
    public static final String FILENAME = "contacts.csv";
    }
DataGeneratorクラスは、従業員に関する情報をランダムに生成します。たとえば、従業員の名前、自宅の住所、勤務先の住所、自宅の電話番号、勤務先の電話番号および従業員の年齢などの情報です。情報は、contacts.csvという名前のCSV形式のファイルに保存されます。このファイルはCSV形式のファイルの標準ルールに従っているため、1行ごとに1つのレコードが記載され、レコード内の個々のフィールドはカンマで区切られています。
例5-4は、contacts.csvファイルの最初の数エントリを示しています。
例5-4 contacts.csvファイルの内容
John,Dvcqbvcp,669 Beacon St.,,Tetuvusz,CA,68457,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,KS,30344,US,home,11,98,183,8707139,work,11,96,425,1175949,63072000000 John,Fxsr,549 Beacon St.,,Jutaswmaby,MI,16315,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,CT,60309,US,home,11,20,40,3662989,work,11,41,187,3148474,-189216000000 John,Gmyrolvfyd,73 Beacon St.,,Lpnztf,AR,82667,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,NY,42297,US,home,11,22,17,8579970,work,11,35,338,9286245,-567648000000 John,Efmpjlbj,85 Beacon St.,,Wyswpb,AK,29590,US,Yoyodyne Propulsion Systems,330 Lectroid Rd.,Grover's Mill,NJ,06219,US,home,11,20,744,4331451,work,11,39,911,9104590,-157680000000 ...
LoaderExampleという名前のJavaクラスを作成します。詳細は、「Javaクラスの作成」を参照してください。
クラスを実装して、「データ・ジェネレータの作成」の項で説明したプログラムで生成された従業員のデータを含むキャッシュをロードします。入力ストリームおよびバッファ済リーダーを使用して、contacts.csvファイルの従業員の情報を単一のCoherenceキャッシュにロードします。
データ・ファイルの従業員の情報を解析するコードを追加します。この情報を取得したら、個々の連絡先を作成してキャッシュに挿入します。処理作業を軽減し、ネットワーク・トラフィックを最低限に抑えるには、putAllメソッドを使用してキャッシュをロードします。
例5-5は、考えられるLoaderExampleクラスの実装を示しています。
例5-5 キャッシュ・ロード・プログラムのサンプル
package com.oracle.handson;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
 
import com.oracle.handson.ContactId;
import com.oracle.handson.Address;
import com.oracle.handson.PhoneNumber;
import com.oracle.handson.Contact;
 
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
 
import java.sql.Date;
 
import java.util.HashMap;
import java.util.Map;
 
 
/**
* LoaderExample loads contacts into the cache from a file.
*
*/
public class LoaderExample
    {
    // ----- static methods -------------------------------------------------
 
    /**
    * Load contacts.
    */
    public static void main (String[] asArg)
            throws IOException
        {
        String sFile  = asArg.length > 0 ? asArg[0] : DataGenerator.FILENAME;
        String sCache = asArg.length > 1 ? asArg[1] : CACHENAME;
 
        System.out.println("input file: " + sFile);
        System.out.println("cache name: " + sCache);
 
        new LoaderExample().load(CacheFactory.getCache(sCache),
                new FileInputStream(sFile));
        CacheFactory.shutdown();
        }
 
    /**
    * Load cache from stream.
    *
    */
    public void load(NamedCache cache, InputStream in)
            throws IOException
        {
        BufferedReader reader   = new BufferedReader(new InputStreamReader(in));
        Map            mapBatch = new HashMap(1024);
        String         sRecord;
        int            cRecord  = 0;
 
        while ((sRecord = reader.readLine()) != null)
            {
            // parse record
            String[]      asPart     = sRecord.split(",");
            int           ofPart     = 0;
            String        sFirstName =  asPart[ofPart++];
            String        sLastName  =  asPart[ofPart++];
            ContactId          id    = new ContactId(sFirstName, sLastName);
            Address       addrHome   = new Address(
                                         /*streetline1*/ asPart[ofPart++],
                                         /*streetline2*/ asPart[ofPart++],
                                         /*city*/        asPart[ofPart++],
                                         /*state*/       asPart[ofPart++],
                                         /*zip*/         asPart[ofPart++],
                                         /*country*/     asPart[ofPart++]);
            Address       addrWork   = new Address(
                                         /*streetline1*/ asPart[ofPart++],
                                         /*streetline2*/ asPart[ofPart++],
                                         /*city*/        asPart[ofPart++],
                                         /*state*/       asPart[ofPart++],
                                         /*zip*/         asPart[ofPart++],
                                         /*country*/     asPart[ofPart++]);
            Map           mapTelNum  = new HashMap();
 
            for (int c = asPart.length - 1; ofPart < c; )
                {
                mapTelNum.put(/*type*/ asPart[ofPart++], new PhoneNumber(
                    /*access code*/  Short.parseShort(asPart[ofPart++]),
                    /*country code*/ Short.parseShort(asPart[ofPart++]),
                    /*area code*/    Short.parseShort(asPart[ofPart++]),
                    /*local num*/    Integer.parseInt(asPart[ofPart++])));
                }
            Date dtBirth = new Date(Long.parseLong(asPart[ofPart]));
 
            // Construct Contact and add to batch
            Contact con1 = new Contact(sFirstName, sLastName, addrHome, 
                                       addrWork, mapTelNum, dtBirth);
            System.out.println(con1);
            mapBatch.put(id, con1);
            
            ++cRecord;
            if  (cRecord % 1024 == 0)
                {
                // load batch
                cache.putAll(mapBatch);
                mapBatch.clear();
                System.out.print('.');
                System.out.flush();
                }
            }
 
            if (!mapBatch.isEmpty())
               {
               // load final batch
               cache.putAll(mapBatch);
               }
 
        System.out.println("Added " + cRecord + " entries to cache");
        }
 
 
    // ----- constants ------------------------------------------------------
 
    /**
    * Default cache name.
    */
    public static final String CACHENAME = "ContactsCache";
 
}
キャッシュ・ロードのサンプルを実行するには:
実行中のキャッシュ・サーバーがあれば停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
ContactsCacheServerファイルを使用して、キャッシュ・サーバーを起動します。
プロジェクトを右クリックして、「Run As」→「Run Configurations」を選択し、ContactsCacheServer構成を編集します。「Main」タブで、「Browse」をクリックしてLoadingプロジェクトを選択します。
「Classpath」タブで、「User Entries」を選択して「Add Projects」をクリックします。「Project Selection」ダイアログ・ボックスで、Loadingプロジェクトを選択します。「OK」をクリックします。
「Common」タブで、「Shared file」をクリックしてLoadingプロジェクトを参照します。
「Apply」→「Run」をクリックします。
DataGeneratorの実行構成を作成します。「Project Explorer」のDataGeneratorを右クリックし、「Run As」を選択します。「Run Configurations」ダイアログ・ボックスで、「Oracle Coherence」を選択して「New Configuration」アイコンをクリックします。
「Name」フィールドにDataGeneratorと入力します。
「Main」タブの「Project」フィールドにLoadingと入力します。「Main class」フィールドにcom.oracle.handson.DataGeneratorと入力します。
「Coherence」タブの「General」タブで、「Cache configuration descriptor」フィールドのc:\home\oracle\workspace\Contacts\appClientModule\coherence-cache-config.xmlファイルを参照します。「Disabled (cache client)」ボタンを選択します。「Cluster port」フィールドに3155と入力します。「Apply」をクリックします。
「Other」タブで、「tangosol.pof.config」フィールドまで下方へスクロールします。POF構成ファイルcontacts-pof-config.xmlへの絶対パスを入力します。「Apply」をクリックします。
「Common」タブで、「Shared file」を選択してLoadingディレクトリを参照します。
「Classpath」タブのLoadingの内容を確認します。図5-3のように、「Contacts」が「Loading」の下にエントリの1つとして表示されます。
LoaderExampleの実行構成を作成します。
「Name」フィールドにLoaderGeneratorと入力します。
「Main」タブの「Project」フィールドにLoadingと入力します。「Main class」フィールドにcom.oracle.handson.LoaderExampleと入力します。
「Coherence」タブの「General」タブで、「Cache configuration descriptor」フィールドのc:\home\oracle\workspace\Contacts\appClientModule\coherence-cache-config.xmlファイルを参照します。「Disabled (cache client)」ボタンを選択します。「Cluster port」フィールドに3155と入力します。「Apply」をクリックします。
「Other」タブで、「tangosol.pof.config」フィールドまで下方へスクロールします。POF構成ファイルcontacts-pof-config.xmlへの絶対パスを入力します。「Apply」をクリックします。
「Classpath」タブの内容を確認します。図5-4のように、「Contacts」が「Loading」の下にエントリの1つとして表示されます。
「Run Configurations」ダイアログ・ボックスからDataGenerator構成を実行します。次にLoaderExample構成を実行します。
DataGenerator実行構成によりデータ・ファイルが生成されます。Eclipseコンソールのウィンドウには出力や応答は表示されません。LoaderExample実行構成により図5-6のような従業員の連絡先の情報のストリームがEclipseコンソール・ウィンドウに送信されます。
例5-6 サンプル・キャッシュ・ロード・プログラムの出力
...
2011-03-15 17:07:51.413/2.266 Oracle Coherence GE 3.7.0.0 <D5> (thread=Invocation:Management, member=2): Service Management joined the cluster with senior service member 1
2011-03-15 17:07:51.413/2.266 Oracle Coherence GE 3.7.0.0 <Info> (thread=Cluster, member=2): Loaded POF configuration from "file:/C:/home/oracle/workspace/Contacts/appClientModule/contacts-pof-config.xml"
2011-03-15 17:07:51.413/2.266 Oracle Coherence GE 3.7.0.0 <Info> (thread=Cluster, member=2): Loaded included POF configuration from "jar:file:/C:/coherence360/coherence/lib/coherence.jar!/coherence-pof-config.xml"
2011-03-15 17:07:51.413/2.266 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=2): Service PartitionedPofCache joined the cluster with senior service member 1
2011-03-15 17:07:51.413/2.266 Oracle Coherence GE 3.7.0.0 <D4> (thread=DistributedCache:PartitionedPofCache, member=2): Asking member 1 for 128 primary partitions
John Hwolru
Addresses
Home: 742 Beacon St.
 
Icymz, OH 74135
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, WY 20222
US
Telephone Numbers
work: +11 50 928 2637858
home: +11 72 403 7946780
Birth Date: 1981-12-28
...
John Unglg
Addresses
Home: 973 Beacon St.
 
Cgarhej, MI 10517
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, GA 81835
US
Telephone Numbers
work: +11 8 925 5233805
home: +11 95 108 2947077
Birth Date: 1965-01-01
John Lkkwgw
Addresses
Home: 806 Beacon St.
 
Zlpft, GU 55786
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, WV 88125
US
Telephone Numbers
work: +11 45 321 6385646
home: +11 87 371 2109053
Birth Date: 1987-12-27
Added 1000 entries to cache
2011-03-15 17:07:51.413/2.266 Oracle Coherence GE 3.7.0.0 <D5> (thread=Invocation:Management, member=2): Service Management left the cluster
2011-03-15 17:07:51.413/2.266 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=2): Service PartitionedPofCache left the cluster
2011-03-15 17:07:51.444/2.297 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=2): Service Cluster left the cluster
この演習では、キャッシュ内のデータの問合せおよび集計について紹介します。この演習では、次の方法を説明します。
キャッシュでの特定データの問合せ
キャッシュ内の情報の集計
名前を持つキャッシュに複合オブジェクトを挿入すると、グリッド内の情報を問い合せたり集計したりできます。com.tangosol.util.QueryMapインタフェースにより、キャッシュ内の値やキーを管理できます。フィルタを使用して結果を制限できます。また、索引を定義して問合せを最適化することもできます。
Coherenceでは格納時に情報がシリアライズされるため、情報の問合せの際にデシリアライズによるオーバーヘッドも発生します。索引が追加されると、索引自体に保存されている値はデシリアライズされるため、アクセスが速くなります。索引付けされるオブジェクトは、常にシリアライズされています。
QueryMapインタフェースには、よく使用される次のようなメソッドがあります。
Set entrySet(Filterfilter)は、マップ内のエントリのうちフィルタ条件を満たす一連のエントリを返します。
addIndex(ValueExtractorextractor,booleanfOrdered, Comparator comparator)は、索引を追加します。
Set keySet(Filter filter)は、entrySetに似ていますが、値ではなくキーを返します。
フィルタリングはキャッシュ・エントリ・オーナー・レベルで発生することに注意してください。パーティション化されたトポロジでは、フィルタリングを実行するのはプライマリ・パーティションであるため、フィルタリングは並行して実行できます。QueryMapインタフェースでは、Filterクラスが使用されます。これらのクラスの詳細は、com.tangosol.util.filterパッケージのAPIを参照してください。
CoherenceのNamedCacheオブジェクトはすべて、com.tangosol.util.QueryMapインタフェースを実装します。これにより、NamedCacheオブジェクトでは、キャッシュ内の指定された条件を満たすキーやエントリの検索がサポートされます。この条件は、com.tangosol.util.Filterインタフェースを実装するオブジェクトとして表すことができます。
com.tangosol.util.filterパッケージには、標準的な問合せ式を実装する事前定義された複数のクラスが含まれています。このようなクラスの例には、GreaterFilter、GreaterEquals、LikeFilter、NotEqualsFilter、InFilterなどがあります。これらのフィルタを使用して、大半のSQL WHERE句式をオブジェクトベースで表現した式を構成できます。
| 注意:Coherenceには SQLFilterがありません。これは、キャッシュ内に配置されたオブジェクトがリレーショナル形式で、つまり行と列を使用してモデル化される可能性が低いためです(オブジェクトは通常、データベースで表現されます)。さらに、キャッシュ内に配置されたオブジェクトは、通常、大型のBLOBSなどのリレーショナル・モデルを使用して容易にモデル化できません。 | 
Filterクラスは、標準的なJavaメソッド・リフレクションを使用してテスト条件を表現します。たとえば、次のフィルタは、キャッシュ内のオブジェクトでgetHomeAddress.getStateメソッドから返される値がMassachusetts (MA)に対する値であるという条件を表しています。
(new EqualsFilter("getHomeAddress.getState", "MA");
このフィルタを使用してテストしたオブジェクトにgetメソッドが指定されていない場合、テストは失敗します。
Filterクラスを使用したその他の例は、次のとおりです。
名前がSで始まる都市に住む人のセットを返します。
Set sPeople = cache.entrySet(new LikeFilter("getHomeAddress.getCity","S%");
年齢が42歳を超える人のセットを返します。
final int nAge = 42;// Find all contacts who are older than nAge
Set sSeniors = cache.entrySet(new GreaterFilter("getAge", nAge));
QueryMapインタフェースで定義されたentrySetメソッドとkeySetメソッド以外に、CoherenceではaddIndexメソッドを使用して索引を定義し、問合せのパフォーマンスを改善できます。よく知られている、厳密に適用された名前付き列のコレクション(スキーマ)に従って索引を定義する、リレーショナル・データベース・システムとは異なり、Coherenceにはスキーマがありません。スキーマがないため、従来のデータベースとは異なる方法で索引を定義します。
キャッシュに配置されている各オブジェクトに対する索引を付ける値を定義するために、Coherenceではvalue extractorの概念が導入されています。com.tangosol.util.ValueExtractorインタフェースはextractメソッドを定義します。オブジェクトのパラメータを指定すると、ValueExtractor実装はパラメータに基づいて値を返します。
ValueExtractor実装の簡単な例であるcom.tangosol.util.extractor.ReflectionExtractorインタフェースでは、リフレクションを使用して、オブジェクトに対するメソッド・コールの結果を返します。たとえば、次のとおりです。
new ReflectionExtractor("getCity")
値エクストラクタは、Coherence API全体で使用できます。ただし通常は、索引を定義するために使用されます。
特に便利なエクストラクタのタイプは、ChainedExtractorです。これは、エクストラクタの配列に基づく複合的なValueExtractor実装です。配列内のエクストラクタは、左から右に順次適用されます。前のエクストラクタの結果が次のエクストラクタのターゲット・オブジェクトになります。たとえば、次のとおりです。
new ChainedExtractor(new ReflectionExtractor("getHomeAddress"), new ReflectionExtractor("getState"))
この例では、HomeAddressオブジェクトとStateオブジェクトが複合Contactオブジェクトに属していることが前提となっています。ChainedExtractorは、まずリフレクションを使用して、キャッシュされた各ContactオブジェクトにgetHomeAddressメソッドをコールします。次に、リフレクションを使用して、返されたHomeAddressオブジェクトのセットにgetStateメソッドをコールします。
問合せを実行するQueryExampleという新規Javaクラスを作成します。このクラスにmainメソッドが存在することを確認します。詳細は、「Javaクラスの作成」を参照してください。
entrySetメソッドを使用して、次のように従業員の連絡先情報を取得します。
マサチューセッツに住むすべての従業員:
cache.entrySet(new EqualsFilter("getHomeAddress.getState", "MA"));
マサチューセッツに住み、勤務先が別の場所であるすべての従業員:
cache.entrySet(new AndFilter(
         new EqualsFilter("getHomeAddress.getState", "MA"),
         new NotEqualsFilter("getWorkAddress.getState", "MA")));
名前がSで始まる都市に住むすべての従業員:
cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%"));
姓がSで始まり、マサチューセッツに住むすべての従業員。問合せにキーと値の両方を使用します。キャッシュ・エントリでは、キーとしてContactIdオブジェクトが使用されます。KeyExtractorを使用して、これらの値を取得できます。KeyExtractorは値ではなく主要なオブジェクトに対して問合せを実行する必要があることを示す、特別な値エクストラクタ:
cache.entrySet(new AndFilter(
     new LikeFilter(new KeyExtractor("getLastName"), "S%", 
         (char) 0, false),
     new EqualsFilter("getHomeAddress.getState", "MA")));
指定された年齢を超えるすべての従業員。ヒント:
final int nAge = 42;
    setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
索引を使用して、パフォーマンスを向上させます。ヒント: QueryMapインタフェースのJavadocでaddIndexメソッドに関する記述を検索します。
例 5-7は、考えられるQueryExampleクラスの実装を示しています。
例5-7 QueryExampleクラスのサンプル
package com.oracle.handson;
 
import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
 
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.KeyExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
 
import com.tangosol.util.filter.AlwaysFilter;
import com.tangosol.util.filter.AndFilter;
import com.tangosol.util.filter.EqualsFilter;
import com.tangosol.util.filter.GreaterFilter;
import com.tangosol.util.filter.LikeFilter;
import com.tangosol.util.filter.NotEqualsFilter;
 
import java.util.Iterator;
import java.util.Set;
 
 
/**
* QueryExample runs sample queries for contacts.
*
*/
public class QueryExample{
  
    // ----- QueryExample methods ---------------------------------------
 
    public static void main(String[] args) {
      NamedCache cache = CacheFactory.getCache("ContactsCache");
       query(cache);
    }
    /**
    * Perform the example queries
    *
    */
     public static void query(NamedCache cache)
        {
         // Add indexes to make queries more efficient
        ReflectionExtractor reflectAddrHome =
                new ReflectionExtractor("getHomeAddress");
        // Add an index for the age
        cache.addIndex(new ReflectionExtractor("getAge"), true, null);
        // Add index for state within home address
       cache.addIndex(new ChainedExtractor(reflectAddrHome,
                new ReflectionExtractor("getState")), true, null);
        // Add index for state within work address
        cache.addIndex(new ChainedExtractor(
                new ReflectionExtractor("getWorkAddress"),
                new ReflectionExtractor("getState")), true, null);
        // Add index for city within home address
        cache.addIndex(new ChainedExtractor(reflectAddrHome,
                new ReflectionExtractor("getCity")), true, null);
 
        // Find all contacts who live in Massachusetts
        Set setResults = cache.entrySet(new EqualsFilter(
                "getHomeAddress.getState", "MA"));
        printResults("MA Residents", setResults);
 
        // Find all contacts who live in Massachusetts and work elsewhere
        setResults = cache.entrySet(new AndFilter(
                new EqualsFilter("getHomeAddress.getState", "MA"),
                new NotEqualsFilter("getWorkAddress.getState", "MA")));
        printResults("MA Residents, Work Elsewhere", setResults);
 
        // Find all contacts whose city name begins with 'S'
        setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity",
                "S%"));
        printResults("City Begins with S", setResults);
 
        final int nAge = 42;
        // Find all contacts who are older than nAge
        setResults = cache.entrySet(new GreaterFilter("getAge", nAge));
        printResults("Age > " + nAge, setResults);
 
        // Find all contacts with last name beginning with 'S' that live
        // in Massachusetts. Uses both key and value in the query.
        setResults = cache.entrySet(new AndFilter(
                new LikeFilter(new KeyExtractor("getLastName"), "S%",
                               (char) 0, false),
                new EqualsFilter("getHomeAddress.getState", "MA")));
        printResults("Last Name Begins with S and State Is MA", setResults);
 
        }
 
    /**
    * Print results of the query
    *
    * @param sTitle      the title that describes the results
    * @param setResults  a set of query results
    */
    private static void printResults(String sTitle, Set setResults)
        {
        System.out.println(sTitle);
        for (Iterator iter = setResults.iterator(); iter.hasNext(); )
            {
            System.out.println(iter.next());
            }
        }
    }
問合せサンプルを実行するには:
すべての稼動しているキャッシュ・サーバーを停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
QueryExampleクラスに対する実行構成を作成します。
「Project Navigator」でQueryExampleを右クリックし、「Run As」→「Run Configurations」を選択します。「Oracle Coherence」を選択して、「New launch configuration」アイコンをクリックします。
「Name」フィールドにQueryExampleと入力します。
「Main」タブの「Project」フィールドにLoadingと入力します。「Main class」フィールドにcom.oracle.handson.QueryExampleと入力します。
「Coherence」タブの「General」タブで、「Cache configuration descriptor」フィールドのc:\home\oracle\workspace\Contacts\appClientModule\coherence-cache-config.xmlファイルを参照します。「Disabled (cache client)」ボタンを選択します。「Cluster port」フィールドに3155と入力します。「Apply」をクリックします。
「Other」タブで、「tangosol.pof.config」フィールドまで下方へスクロールします。POF構成ファイルcontacts-pof-config.xmlへの絶対パスを入力します。「Apply」をクリックします。
「Classpath」タブの内容を確認します。Contactsプロジェクトが、「User Entries」のLoadingプロジェクトの下に表示されます。
ContactsCacheServerを再起動します。
DataGeneratorクラス、LoaderExampleクラスおよびQueryExampleクラスを実行します。
キャッシュ内のすべての連絡先情報を印刷すると、QueryExampleファイルで問合せの結果が表示されます。結果は、例5-8のようになります。
例5-8 QueryExampleプログラムの結果
...
MasterMemberSet
  (
  ThisMember=Member(Id=3, Timestamp=2011-03-15 17:36:07.335, Address=130.35.99.213:8090, MachineId=49877, Location=site:us.oracle.com,machine:tpfaeffl-lap7,process:188, Role=OracleHandsonQueryExample)
  OldestMember=Member(Id=1, Timestamp=2011-03-15 17:35:09.585, Address=130.35.99.213:8088, MachineId=49877, Location=site:us.oracle.com,machine:tpfaeffl-lap7,process:5704, Role=CoherenceServer)
  ActualMemberSet=MemberSet(Size=2, BitSetCount=2
    Member(Id=1, Timestamp=2011-03-15 17:35:09.585, Address=130.35.99.213:8088, MachineId=49877, Location=site:us.oracle.com,machine:tpfaeffl-lap7,process:5704, Role=CoherenceServer)
    Member(Id=3, Timestamp=2011-03-15 17:36:07.335, Address=130.35.99.213:8090, MachineId=49877, Location=site:us.oracle.com,machine:tpfaeffl-lap7,process:188, Role=OracleHandsonQueryExample)
    )
  RecycleMillis=1200000
  RecycleSet=MemberSet(Size=0, BitSetCount=0
    )
  )
 
TcpRing{Connections=[1]}
IpMonitor{AddressListSize=0}
 
2011-03-15 17:36:07.553/1.296 Oracle Coherence GE 3.7.0.0 <D5> (thread=Invocation:Management, member=3): Service Management joined the cluster with senior service member 1
2011-03-15 17:36:07.647/1.390 Oracle Coherence GE 3.7.0.0 <Info> (thread=DistributedCache:PartitionedPofCache, member=3): Loaded POF configuration from "file:/C:/home/oracle/workspace/Contacts/appClientModule/contacts-pof-config.xml"
2011-03-15 17:36:07.663/1.406 Oracle Coherence GE 3.7.0.0 <Info> (thread=DistributedCache:PartitionedPofCache, member=3): Loaded included POF configuration from "jar:file:/C:/oracle/product/coherence/lib/coherence.jar!/coherence-pof-config.xml"
2011-03-15 17:36:07.710/1.453 Oracle Coherence GE 3.7.0.0 <D5> (thread=DistributedCache:PartitionedPofCache, member=3): Service PartitionedPofCache joined the cluster with senior service member 1
MA Residents
ConverterEntry{Key="John Ueccc", Value="John Ueccc
Addresses
Home: 285 Beacon St.
 
Oxtqwisgti, MA 41063
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, VA 28107
US
Telephone Numbers
work: +11 48 835 1876678
home: +11 78 482 1247744
Birth Date: 1972-12-30"}
...
MA Residents, Work Elsewhere
ConverterEntry{Key="John Ueccc", Value="John Ueccc
Addresses
Home: 285 Beacon St.
 
Oxtqwisgti, MA 41063
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, VA 28107
US
Telephone Numbers
work: +11 48 835 1876678
home: +11 78 482 1247744
Birth Date: 1972-12-30"}
...
City Begins with S
ConverterEntry{Key="John Frepojf", Value="John Frepojf
Addresses
Home: 851 Beacon St.
 
Swnsfng, PR 00734
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, AR 97794
US
Telephone Numbers
work: +11 10 474 781020
home: +11 29 575 9284939
Birth Date: 1985-12-27"}
...
Age > 42
ConverterEntry{Key="John Qghbguyn", Value="John Qghbguyn
Addresses
Home: 49 Beacon St.
 
Dvftzpq, PR 34220
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, MD 28247
US
Telephone Numbers
work: +11 39 563 7113013
home: +11 58 910 4667915
Birth Date: 1961-01-02"}
...
Last Name Begins with S and State Is MA
ConverterEntry{Key="John Smnfg", Value="John Smnfg
Addresses
Home: 178 Beacon St.
 
Hbpeak, MA 64081
US
Work: Yoyodyne Propulsion Systems
330 Lectroid Rd.
Grover's Mill, OK 92191
US
Telephone Numbers
work: +11 56 322 7307404
home: +11 33 928 3075361
Birth Date: 1978-12-29"}
...
2011-03-15 17:36:08.350/2.093 Oracle Coherence GE 3.7.0.0 <D4> (thread=ShutdownHook, member=3): ShutdownHook: stopping cluster node
コードをQueryExample.javaファイルに追加して、キャッシュ・データに対して集計を実行します。エントリ・アグリゲータ(com.tangosol.util.InvocableMap.EntryAggregator)を使用すると、すべてのオブジェクトまたは特定のオブジェクトのセットに対して操作を実行し、集計を返すことができます。EntryAggregatorインタフェースは、クラスタ内のデータに対して並行してサービスを実行するエージェントです。集計は並行して実行され、クラスタ・メンバーの追加によるメリットを得ることができます。
集計には、キーのコレクションに対する集計と、フィルタの指定による集計の2つの方法があります。例5-9は、これらのタスクを実行するEntryAggregatorメソッドを示しています。
例5-9 キーに対する集計メソッドとフィルタ指定による集計メソッド
Object aggregate(Collection keys, InvocableMap.entryAggregator agg) Object aggregate(Filter filter, InvocableMap.entryAggregator agg)
フィルタリングに対してデータを返すために集計を追加するには:
集計を使用し、QueryExampleクラスにコードを記述して次の項目を計算します。
指定した年齢を超える従業員の数。GreaterFilterクラスおよびCountクラスを使用します。
cache.aggregate(new GreaterFilter("getAge", nAge), new Count())
従業員セット内の最低年齢。AlwaysFilterクラスおよびLongMinクラスを使用使用します。
cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge"))
従業員セット内の最高年齢。AlwaysFilterクラスおよびLongMaxクラスを使用します。
cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge"))
従業員の平均年齢。AlwaysFilterクラスおよびDoubleAverageクラスを使用します。
cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge")
Count、DoubleAverage、LongMaxおよびLongMinアグリゲータ・クラスをインポートします。
import com.tangosol.util.aggregator.Count; import com.tangosol.util.aggregator.DoubleAverage; import com.tangosol.util.aggregator.LongMax; import com.tangosol.util.aggregator.LongMin;
QueryExample.javaファイルは例5-10のようになります。
例5-10 集計を使用したQueryExample
package com.oracle.handson; import com.tangosol.net.CacheFactory; import com.tangosol.net.NamedCache;import com.tangosol.util.aggregator.Count;import com.tangosol.util.aggregator.DoubleAverage;import com.tangosol.util.aggregator.LongMax;import com.tangosol.util.aggregator.LongMin;import com.tangosol.util.extractor.ChainedExtractor; import com.tangosol.util.extractor.KeyExtractor; import com.tangosol.util.extractor.ReflectionExtractor; import com.tangosol.util.filter.AlwaysFilter; import com.tangosol.util.filter.AndFilter; import com.tangosol.util.filter.EqualsFilter; import com.tangosol.util.filter.GreaterFilter; import com.tangosol.util.filter.LikeFilter; import com.tangosol.util.filter.NotEqualsFilter; import java.util.Iterator; import java.util.Set; /** * QueryExample runs sample queries for contacts. */ public class QueryExample{ // ----- QueryExample methods --------------------------------------- public static void main(String[] args) { NamedCache cache = CacheFactory.getCache("ContactsCache"); query(cache); } /** * Perform the example queries * */ public static void query(NamedCache cache) { // Add indexes to make queries more efficient ReflectionExtractor reflectAddrHome = new ReflectionExtractor("getHomeAddress"); cache.addIndex(new ReflectionExtractor("getAge"), true, null); cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getState")), true, null); cache.addIndex(new ChainedExtractor( new ReflectionExtractor("getWorkAddress"), new ReflectionExtractor("getState")), true, null); cache.addIndex(new ChainedExtractor(reflectAddrHome, new ReflectionExtractor("getCity")), true, null); // Find all contacts who live in Massachusetts Set setResults = cache.entrySet(new EqualsFilter( "getHomeAddress.getState", "MA")); printResults("MA Residents", setResults); // Find all contacts who live in Massachusetts and work elsewhere setResults = cache.entrySet(new AndFilter( new EqualsFilter("getHomeAddress.getState", "MA"), new NotEqualsFilter("getWorkAddress.getState", "MA"))); printResults("MA Residents, Work Elsewhere", setResults); // Find all contacts whose city name begins with 'S' setResults = cache.entrySet(new LikeFilter("getHomeAddress.getCity", "S%")); printResults("City Begins with S", setResults); final int nAge = 42; // Find all contacts who are older than nAge setResults = cache.entrySet(new GreaterFilter("getAge", nAge)); printResults("Age > " + nAge, setResults); // Find all contacts with last name beginning with 'S' that live // in Massachusetts. Uses both key and value in the query. setResults = cache.entrySet(new AndFilter( new LikeFilter(new KeyExtractor("getLastName"), "S%", (char) 0, false), new EqualsFilter("getHomeAddress.getState", "MA"))); printResults("Last Name Begins with S and State Is MA", setResults); // Count contacts who are older than nAge System.out.println("count > " + nAge + ": "+ cache.aggregate( new GreaterFilter("getAge", nAge), new Count())); // Find minimum age System.out.println("min age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMin("getAge"))); // Calculate average age System.out.println("avg age: " + cache.aggregate(AlwaysFilter.INSTANCE, new DoubleAverage("getAge"))); // Find maximum age System.out.println("max age: " + cache.aggregate(AlwaysFilter.INSTANCE, new LongMax("getAge"))); } /** * Print results of the query * */ private static void printResults(String sTitle, Set setResults) { System.out.println(sTitle); for (Iterator iter = setResults.iterator(); iter.hasNext(); ) { System.out.println(iter.next()); } } }
キャッシュを問い合せて結果を集計するには:
Contactsキャッシュ・サーバー、ContactsCacheServerを停止します。詳細は、「キャッシュ・サーバーの停止」を参照してください。
ContactsCacheServerを再起動します。
DataGeneratorアプリケーション、LoaderExampleアプリケーションおよびQueryExampleアプリケーションを実行します。
Eclipseコンソールに、例5-11のように出力されます。
例5-11 アグリゲータからの出力
...Last Name Begins with S and State Is MAConverterEntry{Key="John Sqmyas", Value="John Sqmyas Addresses Home: 594 Beacon St. Jxaxt, MA 10081 US Work: Yoyodyne Propulsion Systems 330 Lectroid Rd. Grover's Mill, NJ 95236 US Telephone Numbers work: +11 70 837 4723938 home: +11 2 227 6816592 Birth Date: 1977-12-29"}count > 42: 482min age: 22avg age: 41.627max age: 612011-03-15 17:47:26.663/2.078 Oracle Coherence GE 3.7.0.0 <D4> (thread=ShutdownHook, member=3): ShutdownHook: stopping cluster node 2011-03-15 17:47:26.663/2.078 Oracle Coherence GE 3.7.0.0 <D5> (thread=Cluster, member=3): Service Cluster left the cluster 2011-03-15 17:47:26.663/2.078 Oracle Coherence GE 3.7.0.0 <D5> (thread=Invocation:Management, member=3): Service Management left the cluster