Portable Object Format(POF)を使用すると、パフォーマンス上のメリットから言語の独自性にいたるまで幅広い利点を得ることができます。Coherenceで作業する際には、シリアライズ・ソリューションとしてPOFを詳細に検討することをお薦めします。.NET拡張クライアントを構築する際のPOFの操作方法の詳細は、『Oracle Coherenceクライアント・ガイド』の「.NETクライアントの統合オブジェクトの構築」を参照してください。C++拡張クライアントを構築する際のPOFの操作方法の詳細は、『Oracle Coherenceクライアント・ガイド』の「C++クライアントの統合オブジェクトの構築」を参照してください。
この章は次の各項で構成されています。
シリアライズとは、オブジェクトをバイナリ形式にエンコードするプロセスです。これは、Coherenceを使用してデータをネットワーク上で移動させることが必要になった場合に重要となる機能です。Portable Object Format(POF)は言語に依存しないバイナリ形式です。POFは領域と時間の両面で効率を向上させるように設計されており、Coherenceの使用時に不可欠なものとなっています。POFバイナリ・ストリームの詳細は、付録F「PIF-POFバイナリ形式」を参照してください。
シリアライズに関して使用可能なオプションには、標準Javaシリアライズ、POF、独自のカスタム・シリアライズ・ルーチンなどいくつかあります。各オプションには、それぞれトレードオフがあります。標準Javaシリアライズは簡単に実装可能であり、循環オブジェクト・グラフをサポートし、オブジェクトIDを保持します。ただし、比較的動作が低速で、冗長なバイナリ形式が使用され、Javaオブジェクトのみが処理対象となります。
一方、Portable Object Formatには次のようなメリットがあります。
現在はJava、.NETおよびC++をサポートしており、言語に依存しません。
非常に効率的です。1つのString、1つのlongおよび3つのintsを備えた簡単なテスト・クラスでは、標準のJavaシリアライズと比較して、シリアライズおよびデシリアライズが7倍高速化され、生成されるバイナリのサイズが1/6になります。
バージョニングが可能です。オブジェクトの向上を図ることが可能であり、上位および下位の互換性があります。
シリアライズ・ロジックを外部化することができます。
索引付けされ、オブジェクト全体をデシリアライズせずに値を抽出できます。「POFエクストラクタとPOFアップデータの使用方法」を参照してください。
POFには、オブジェクトのシリアライズおよびデシリアライズの方法を認識するシリアライズ・ルーチンの実装が必要です。その方法には、次の2通りがあります。
使用するオブジェクトにcom.tangosol.io.pof.PortableObjectインタフェースを実装させます。
com.tangosol.io.pof.PofSerializerインタフェースを使用して、オブジェクトのシリアライザを実装します。
PortableObjectインタフェースは、次の2つのメソッドで構成される単純なインタフェースです。
public void readExternal(PofReader reader)
public void writeExternal(PofWriter writer)
前述のように、POF要素は索引付けされます。そのためには、POFストリームに対して書込みまたは読取りを実行する各要素に数字の索引を付けます。索引はPOFストリームにおいて書込みと読取りが実行される各要素に対して一意である必要があります。索引はスーパー・クラスと派生クラスの間で一意である必要があるため、派生タイプが存在する場合には特に注意が必要です。
例17-1は、PortableObjectインタフェースの簡単な実装例です。
例17-1 PortableObjectインタフェースの実装
public void readExternal(PofReader in) 
        throws IOException 
    {
    m_symbol    = (Symbol) in.readObject(0);
    m_ldtPlaced = in.readLong(1);
    m_fClosed   = in.readBoolean(2);
    }
 
public void writeExternal(PofWriter out) 
        throws IOException 
    {
    out.writeObject(0, m_symbol);
    out.writeLong(1, m_ldtPlaced);
    out.writeBoolean(2, m_fClosed);
    }
PofSerializerインタフェースには、シリアライズ・ロジックをシリアライズするクラスから外部化する手段が用意されています。これは、CoherenceでPOFを使用する場合に、クラスの構造を変更したくないときには特に役立ちます。PofSerializerインタフェースは、次の2つのメソッドで構成されています。
public Object deserializer(PofReader in)
public void serialize(PofWriter out, Object o)
PortableObjectインタフェース同様、POFストリームに対して書込みまたは読取りが実行される要素はすべて一意に索引付けされている必要があります。PofSerializerインタフェースの実装例を、次に示します。
例17-2 PofSerializerインタフェースの実装
public Object deserialize(PofReader in) 
        throws IOException 
    {
    Symbol symbol    = (Symbol)in.readObject(0);
    long   ldtPlaced = in.readLong(1);
    bool   fClosed   = in.readBoolean(2);
    
    // mark that we're done reading the object
    in.readRemainder(null);
 
    return new Trade(symbol, ldtPlaced, fClosed);
    }
 
public void serialize(PofWriter out, Object o) 
        throws IOException 
    {
    Trade trade = (Trade) o;
    out.writeObject(0, trade.getSymbol());
    out.writeLong(1, trade.getTimePlaced());
    out.writeBoolean(2, trade.isClosed());
    
    // mark that we're done writing the object
    out.writeRemainder(null);
    }
POFの索引をオブジェクトの属性に割り当てるときには、次のガイドラインに従ってください。
読取りと書込みの順序: シリアライズ・ルーチンの最低索引値から開始して、最高索引値で終了します。値をデシリアライズする場合には、書込みと同じ順序で読取りを実行します。
非連続の索引を指定することはできますが、読取りまたは書込みが連続して実行される必要があります。
サブクラス作成時に索引の範囲を予約する場合: 索引は派生タイプ間で累積されます。そのため、各派生タイプがそのスーパークラスで予約されたPOF索引範囲を認識する必要があります。
索引を他の目的に使用しない: 進化をサポートするためには、クラス・リビジョン全体で属性の索引を他の目的に使用しないようにすることが必須です。
ラベルの索引: public static final intでラベル付けされている索引は、さらに簡単に使用できます。特に、POFエクストラクタとPOFアップデータを使用する場合には簡単です。「POFエクストラクタとPOFアップデータの使用方法」を参照してください。ラベル付けされた索引の読取りおよび書込みも、上述の説明と同じ順序で実行される必要があります。
Coherenceには、com.tangosol.io.pof.ConfigurablePofContextシリアライザ・クラスが用意されていて、それによりPOFのシリアライズ・オブジェクトが適切なシリアライズ・ルーチン(PofSerializer実装、またはPortableObjectインタフェースを介した呼び出しによる)にマップされます。
クラスにシリアライズ・ルーチンが指定されると、そのクラスはPOF構成ファイルを使用してConfigurablePofContextクラスに登録されます。POF構成ファイルは<user-type-list>要素を持つXMLファイルであり、それにはPortableObjectを実装するクラスまたはそれらに関連付けられているPofSerializerを持つクラスのリストが記載されています。各クラスに対する<type-id>は一意である必要があり、すべてのクラスタ・インスタンス(拡張クライアントを含む)で一致している必要があります。POFの構成要素の詳細は、付録D「POFユーザー定義型の構成要素」を参照してください。
POF構成ファイルの例を次に示します。
<pof-config>
  <user-type-list>
    <include>coherence-pof-config.xml</include>
    ...
    <!-- User types must be above 1000 -->
    <user-type>
      <type-id>1001</type-id>
      <class-name>com.examples.MyTrade</class-name>
      <serializer>
        <class-name>com.examples.MyTradeSerializer</class-name>
      </serializer>
    </user-type>
 
    <user-type>
      <type-id>1002</type-id>
      <class-name>com.examples.MyPortableTrade</class-name>
    </user-type>
  ...
</pof-config>
| 注意:Coherenceでは、最初の1000個のtype-idは内部使用のために予約されています。上の例に示されているように、 <user-type-list>にはcoherence.jarファイルのルートにあるcoherence-pof-config.xmlファイルが含まれます。このリストにはCoherence固有のユーザー型が定義されており、それらのユーザー型はすべてのPOF構成ファイルに含まれている必要があります。 | 
CoherenceでConfigurablePofContextシリアライザ・クラスを使用する場合には、必要な精度のレベルに基づいた、次の3通りの構成が可能です。
サービスごと: 各サービスには、ConfigurablePofContextシリアライザ・クラスの完全な構成が用意されており、オペレーション構成ファイルにある事前定義済の構成が参照されます。
すべてのサービス: すべてのサービスでグローバルConfigurablePofContextシリアライザ・クラスの構成が使用されます。独自の構成を提供するサービスによってグローバルな構成がオーバーライドされます。グローバル構成を完全構成にして、オペレーション構成ファイルに含まれる事前定義済の構成を参照することも可能です。
JVM: ConfigurablePofContextシリアライザ・クラスはJVM全体に対して有効化されます。
ConfigurablePofContextクラスを使用するようにサービスを構成するには、キャッシュ構成ファイルのキャッシュ・スキームに<serializer>要素を追加します。<serializer>要素の完全なリファレンスは、「serializer」を参照してください。
次の例では、ConfigurablePofContextクラスを使用するように構成された分散キャッシュを示しており、カスタムPOF構成ファイルを定義しています。
 <distributed-scheme>
   <scheme-name>example-distributed</scheme-name>
   <service-name>DistributedCache</service-name>
   <serializer>
      <instance>
         <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>
         <init-params>
            <init-param>
               <param-value>my-pof-config.xml</param-value>
               <param-type>String</param-type>
            </init-param>
         </init-params>
      </instance>
   </serializer>
   ...
</distributed-scheme>
次の例では、オペレーション構成ファイルのデフォルトの定義を参照します。デフォルトのConfigurablePofContextシリアライザ定義を確認するには、「serializers」を参照してください。
 <distributed-scheme>
    <scheme-name>example-distributed</scheme-name>
    <service-name>DistributedCache</service-name>
    <serializer>pof</serializer>
    ...
 </distributed-scheme>
ConfigurablePofContextクラスをすべてのサービスに対してグローバルに構成するには、キャッシュ構成ファイルの<defaults>要素内に<serializer>要素を追加します。次の例は両方とも、シリアライザをすべてのキャッシュ・スキームの定義に対してグローバルに構成するものであり、個々のキャッシュ・スキームの定義内にその他の構成を追加する必要はありません。<defaults>要素の完全なリファレンスは、「defaults」を参照してください。
次の例では、ConfigurablePofContextクラスに対するグローバルな構成を示しており、カスタムPOF構成ファイルを定義しています。
<cache-config>
   <defaults>
      <serializer>
         <instance>
            <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>
            <init-params>
               <init-param>
                  <param-value>my-pof-config.xml</param-value>
                  <param-type>String</param-type>
               </init-param>
            </init-params>
         </instance>
      </serializer>
   </defaults>
   ...
次の例では、オペレーション構成ファイルのデフォルトの定義を参照します。デフォルトのConfigurablePofContextシリアライザ定義を確認するには、「serializers」を参照してください。
<cache-config>
   <defaults>
      <serializer>pof</serializer>
   </defaults>
   ...
Coherenceでは、ValueExtractorインスタンスとValueUpdaterインスタンスを使用して、キャッシュに格納されたオブジェクトの値が抽出および更新されます。PofExtractorインスタンスとPofUpdaterインスタンスは、POFの索引付けされた状態を利用して、シリアライズ・ルーチンまたはデシリアライズ・ルーチン全体を実行せずにオブジェクトを抽出または更新します。
PofExtractorとPofUpdaterにより、Coherenceの非プリミティブ・タイプを使用するときの柔軟性が向上します。拡張クライアントを使用する場合には、通常、グリッド内に対応するJavaクラスが存在する必要はありません。POFエクストラクタとPOFアップデータはバイナリをナビゲートできるため、キーと値全体をObjec形式にデシリアライズしなくても済みます。これは、索引付けする値をPOFエクストラクタを使用してプルするだけで索引付けできることを意味します。ただし、次のように、対応するJavaクラスを指定する必要がある場合があります。
キーの対応付け: キーの対応付けを使用する場合、Coherenceでは、常にキーをデシリアライズして、それらのキーがキーの対応付けを実装するかどうかを判断します。
キャッシュ・ストア: キャッシュ・ストアを使用する場合、キーと値のデシリアライズ・バージョンがキャッシュ・ストアに渡され、バックエンドに書き込まれます。
Portable Object Format (POF)に索引が付けられているため、抽出または更新のための特定の要素にバイナリを迅速に横断できます。PofNavigatorインタフェースにより、POF値のオブジェクトを横断させて、必要なPOF値のオブジェクトを返すことができます。既定では、CoherenceにはSimplePofPathクラスが用意されていて、整数の索引に基づいてPOF値を移動できます。最も簡単な形式では、抽出や更新が必要な属性の索引を入力するだけでPOF値を移動できます。次の例を参照してください。
public class Contact
        implements PortableObject
    {
    ...
    // ----- PortableObject interface ---------------------------------------
 
    /**
    * {@inheritDoc}
    */
    public void readExternal(PofReader reader)
            throws IOException
        {
        m_sFirstName     = reader.readString(FIRSTNAME);
        m_sLastName      = reader.readString(LASTNAME);
        m_addrHome       = (Address) reader.readObject(HOME_ADDRESS);
        m_addrWork       = (Address) reader.readObject(WORK_ADDRESS);
        m_mapPhoneNumber = reader.readMap(PHONE_NUMBERS, null);
        }
 
    /**
    * {@inheritDoc}
    */
    public void writeExternal(PofWriter writer)
            throws IOException
        {
        writer.writeString(FIRSTNAME, m_sFirstName);
        writer.writeString(LASTNAME, m_sLastName);
        writer.writeObject(HOME_ADDRESS, m_addrHome);
        writer.writeObject(WORK_ADDRESS, m_addrWork);
        writer.writeMap(PHONE_NUMBERS, m_mapPhoneNumber);
        }
 
    ....
 
    // ----- constants -------------------------------------------------------
 
    /**
    * The POF index for the FirstName property
    */
    public static final int FIRSTNAME = 0;
 
    /**
    * The POF index for the LastName property
    */
    public static final int LASTNAME = 1;
 
    /**
    * The POF index for the HomeAddress property
    */
    public static final int HOME_ADDRESS = 2;
 
    /**
    * The POF index for the WorkAddress property
    */
    public static final int WORK_ADDRESS = 3;
 
    /**
    * The POF index for the PhoneNumbers property
    */
    public static final int PHONE_NUMBERS = 4;
 
    ...
}
POFストリームへの書込みまたはPOFストリームからの書込みを実行中の各データ・メンバーに対する定数が存在することに注意してください。この方法に従えば、POFエクストラクタとPOFアップデータを容易に使用できてシリアライズ・ルーチンの書込みも簡素化されるという、優れた利点が得られます。各索引にラベルを付けることで、索引の操作はさらに容易になります。前述のように、最も簡単な場合では、WORK_ADDRESS索引を使用して連絡先から勤務先のアドレスを抽出することができます。SimplePofPathにより、intsのArrayを使用してPofValuesを横断できるようになります。したがって、たとえば勤務先のアドレスのzip codeが必要な場合には、[WORK_ADDRESS, ZIP]を使用します。次の例でさらに詳しく説明します。
PofExtractorsは、通常、キャッシュに問い合せる場合に使用され、問合せのパフォーマンスを大幅に向上させます。前述の例のクラスを使用して、Jonesという姓のすべての連絡先をキャッシュに問い合せる場合は、次のようになります。
ValueExtractor veName = new PofExtractor(String.class, Contact.LASTNAME); Filter filter = new EqualsFilter(veName, "Jones"); // find all entries that have a last name of Jones Set setEntries = cache.entrySet(filter);
この例では、PofExtractorには便利なコンストラクタがあり、SimplePofPathを使用して1つの索引(この例ではContact.LASTNAMEの索引)を取得します。地域コード01803のすべての連絡を検索する場合は、次のようになります。
ValueExtractor veZip = new PofExtractor(
   String.class, new SimplePofPath(new int[] {Contact.WORK_ADDRESS, Address.ZIP}));
 
Filter filter = new EqualsFilter(veZip, "01803");
 
// find all entries that have a work address in the 01803 zip code
Set setEntries  = cache.entrySet(filter);
前述の例では、PofExtractorコンストラクタには抽出された値またはnullのクラスを持つ最初の引数があります。型情報を渡すのは、POFがシリアライズされた値で圧縮形式を使用するからです(使用可能な場合)。たとえば、一部の数値は、型が値を暗示する特殊なPOF固有型として示されています。その結果、POFの値を受け取る側では、その型を暗黙的に認識できることが必要になります。PofExtractorは、コンストラクタで提供されたクラスを型情報のソースとして使用します。このクラスがnullの場合、PofExtractorはシリアライズされた状態から型を推測しますが、抽出される型は予想した型と異なることがあります。実際には、String型はPOFストリームから適切に推測されるので、前述の例ではnullを使用すれば十分です。ただし、通常はnullを使用しないでください。
PofUpdaterは、オブジェクトの値を抽出するのではなく更新する場合を除いて、PofExtractorと同じように機能します。したがって、Jonesという姓のすべてのエントリをSmithに変更する場合には、次のようにUpdaterProcessorを使用します。
ValueExtractor veName = new PofExtractor(String.class, Contact.LASTNAME); Filter filter = new EqualsFilter(veName, "Jones"); ValueUpdater updater = new PofUpdator(Contact.LASTNAME); // find all Contacts with the last name Jones and change them to have the last // name "Smith" cache.invokeAll(filter, new UpdaterProcessor(updater, "Smith"));
| 注意:この機能は、これらの例では Stringベースの値で動作していますが、POFのエンコードされた値でも動作します。 |