Coherenceキャッシュは、値オブジェクトをキャッシュするために使用されます。.NETクライアントがCoherence JVMと正常に通信するためには、プラットフォームに依存しないシリアライズ形式が必要になります。これにより、.NETクライアントとCoherence JVM(Coherence*Extend Javaクライアントを含む)の両方で、Coherenceキャッシュに格納されている値オブジェクトを正しくシリアライズおよびデシリアライズできるようになります。Coherence for .NETクライアント・ライブラリおよびCoherence*Extendクラスタ・サービスでは、Portable Object Format(POF)というシリアライズ形式が使用されます。POFを使用すると、値オブジェクトのプラットフォームおよび元の言語と関係ない方法で、値オブジェクトをバイナリ・ストリームにエンコードできます。
この章は次の各項で構成されています。
POFでは、共通の.NET型およびJava型がすべてそのままサポートされます。カスタムの.NETクラスおよびJavaクラスもPOFストリームにシリアライズ可能ですが、そのためには次の追加手順が必要となります。
IPortableObjectインタフェースを実装する.NETクラスを作成します (「IPortableObject実装の作成(.NET)」を参照)。
同様に、PortableObjectインタフェースを実装する、対応するJavaクラスを作成します (「PortableObject実装の作成(Java)」を参照)。
カスタムの.NETクラスをクライアントに登録します (「.NETクライアントでのカスタム型の登録」を参照)。
Coherence*Extendクラスタ・サービスを実行している各サーバーにカスタムのJavaクラスを登録します (「クラスタでのカスタム型の登録」を参照)。
ここまでの手順が完了すると、組込みのデータ型と同じようにカスタムの.NETクラスをCoherenceキャッシュにキャッシュできるようになります。さらに対応するJavaクラスを使用すれば、それらの型をCoherenceまたはCoherence*Extend JVMから取得し、操作して、格納することができます。
IPortableObjectを実装する各クラスは、自身の状態をPOFデータ・ストリームにシリアライズし、POFデータ・ストリームからデシリアライズすることができます。この処理は、ReadExternalメソッド(デシリアライズ)およびWriteExternalメソッド(シリアライズ)で行われます。概念上は、すべてのユーザー定義型が0個以上の索引付き値(プロパティ)で構成されており、それらが1つずつPOFデータ・ストリームから読み取られ、POFデータ・ストリームに書き込まれます。移植可能なクラスには、IPortableObjectインタフェースの実装が必要であること以外の唯一の要件として、デフォルト・コンストラクタが必要です。これにより、POFデシリアライザで、デシリアライズ時にクラスのインスタンスを作成できるようになります。
例20-1は、移植可能なユーザー定義クラスを示しています。
例20-1 移植可能なユーザー定義クラス
public class ContactInfo : IPortableObject
{
private string name;
private string street;
private string city;
private string state;
private string zip;
public ContactInfo()
{}
public ContactInfo(string name, string street, string city, string state, string zip)
{
Name = name;
Street = street;
City = city;
State = state;
Zip = zip;
}
public void ReadExternal(IPofReader reader)
{
Name = reader.ReadString(0);
Street = reader.ReadString(1);
City = reader.ReadString(2);
State = reader.ReadString(3);
Zip = reader.ReadString(4);
}
public void WriteExternal(IPofWriter writer)
{
writer.WriteString(0, Name);
writer.WriteString(1, Street);
writer.WriteString(2, City);
writer.WriteString(3, State);
writer.WriteString(4, Zip);
}
// property definitions ommitted for brevity
}
Javaでの移植可能なクラスの実装は、前述の例に示した.NETでの実装と非常によく似ています。
例20-2は、例20-1の.NETクラスをJavaで実現した場合を示しています。
例20-2 Javaでのユーザー定義クラス
public class ContactInfo implements PortableObject
{ private String m_sName;
private String m_sStreet;
private String m_sCity;
private String m_sState;
private String m_sZip;
public ContactInfo()
{
}
public ContactInfo(String sName, String sStreet, String sCity, String sState, String sZip)
{
setName(sName);
setStreet(sStreet);
setCity(sCity);
setState(sState);
setZip(sZip);
}
public void readExternal(PofReader reader)
throws IOException
{
setName(reader.readString(0));
setStreet(reader.readString(1));
setCity(reader.readString(2));
setState(reader.readString(3));
setZip(reader.readString(4));
}
public void writeExternal(PofWriter writer)
throws IOException
{
writer.writeString(0, getName());
writer.writeString(1, getStreet());
writer.writeString(2, getCity());
writer.writeString(3, getState());
writer.writeString(4, getZip());
}
// accessor methods omitted for brevity
}
各POFユーザー定義型は、POFストリーム内で整数値として表されます。したがって、POFには、ユーザー定義型をそのエンコードされた型識別子に(またはその逆方向に)マップするための外部メカニズムが必要です。このメカニズムでは、XML構成ファイルを使用してマッピング情報が格納されます。これを例20-3に示します。これらの要素の詳細は、『Oracle Coherence開発者ガイド』の「POFユーザー定義型の構成要素」を参照してください。
例20-3 POFユーザー定義型構成ファイルへのマッピング情報の格納
<?xml version="1.0"?>
<pof-config xmlns="http://schemas.tangosol.com/pof">
<user-type-list>
<!-- include all "standard" Coherence POF user types -->
<include>assembly://Coherence/Tangosol.Config/coherence-pof-config.xml</include>
<!-- include all application POF user types -->
<user-type>
<type-id>1001</type-id>
<class-name>My.Example.ContactInfo, MyAssembly</class-name>
</user-type>
...
</user-type-list>
</pof-config>
次の点に注意してください。
カスタム型の型識別子は1001以上の数値で始まる必要があります。1000より小さい数値は内部使用に予約されています。この例に示すように、<user-type-list>にはcoherence-pof-config.xmlファイルが含まれます。これにはCoherence固有のユーザー型が定義されており、そのユーザー型をすべてのPOF構成ファイルに含める必要があります。
class-name要素内では完全に修飾された型名を指定する必要はありません。型とアセンブリ名を指定すれば十分です。
型識別子とカスタム型の間のマッピングを構成したら、キャッシュ構成ディスクリプタにserializer要素を追加して、それらのマッピングを使用するようCoherence for .NETを構成する必要があります。たとえば、例20-3に示したユーザー定義型のマッピングがmy-dotnet-pof-config.xmlに保存されている場合は、例20-4に示すようにserializer要素を指定する必要があります。
例20-4 キャッシュ構成ファイルでのserializerの使用
<remote-cache-scheme>
<scheme-name>extend-direct</scheme-name>
<service-name>ExtendTcpCacheService</service-name>
<initiator-config>
...
<serializer>
<class-name>Tangosol.IO.Pof.ConfigurablePofContext, Coherence</class-name>
<init-params>
<init-param>
<param-type>string</param-type>
<param-value>my-dotnet-pof-config.xml</param-value>
</init-param>
</init-params>
</serializer>
</initiator-config>
</remote-cache-scheme>
シリアライザを明示的に指定しない場合は、ConfigurablePofContext型がPOFシリアライザとして使用されます。これはpof-config.xmlというデフォルトの構成ファイルを使用します。Coherence .NETアプリケーションでは、アプリケーションのデプロイ先フォルダと、Webアプリケーションの場合はWebアプリケーションのルートの両方で、デフォルトのPOF構成ファイルが検索されます。POF構成ファイルが見つからない場合は、Coherence for .NETアプリケーション構成ファイルのpof-config要素の内容による特定が試行されます。例:
TCP/IP Coherence*Extendクラスタ・サービスを実行しているCoherenceノードでカスタム型のオブジェクトを送受信できるようにするには、各ノードに、カスタム型の類似したPOF構成ファイルが必要です。
クラスタ側のPOF構成ファイルは、クライアントで作成された構成ファイルと似ています。唯一の違いは、class-name要素で.NETクラス名のかわりに完全修飾されたJavaクラス名を指定する必要がある点です。
例20-6は、クラスタ側のPOF構成ファイルのサンプル(my-java-pof-config.xml)を示しています。
例20-6 クラスタ側のPOF構成ファイル
<?xml version="1.0"?>
<!DOCTYPE pof-config SYSTEM "pof-config.dtd">
<pof-config>
<user-type-list>
<!-- include all "standard" Coherence POF user types -->
<include>coherence-pof-config.xml</include>
<!-- include all application POF user types -->
<user-type>
<type-id>1001</type-id>
<class-name>com.mycompany.example.ContactInfo</class-name>
</user-type>
...
</user-type-list>
</pof-config>
カスタム型を追加したら、オブジェクトのシリアライズ時に各自のPOF構成ファイルが使用されるようサーバーを構成する必要があります。これを例20-7に示します。
例20-7 POF構成ファイルを使用するためのサーバーの構成
<proxy-scheme>
<service-name>ExtendTcpProxyService</service-name>
<acceptor-config>
...
<serializer>
<class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>
<init-params>
<init-param>
<param-type>string</param-type>
<param-value>my-java-pof-config.xml</param-value>
</init-param>
</init-params>
</serializer>
</acceptor-config>
...
</proxy-scheme>
PIF-POFは、シリアライズされた形式の移植可能なユーザー定義型の上位互換性および下位互換性をネイティブにサポートします。.NETでは、ユーザー定義型でIPortableObjectインタフェースのかわりにIEvolvablePortableObjectインタフェースを実装することで、このサポートが実現します。IEvolvablePortableObjectインタフェースは、IPortableObjectとIEvolvableの両方のインタフェースを拡張するマーカー・インタフェースです。IEvolvableインタフェースは、型のバージョニングをサポートする3つのプロパティを追加します。IEvolvableクラスには整数のバージョン識別子n(n >= 0)が含まれています。シリアライズされた形式のIEvolvableクラスのコンテンツやセマンティクスが変更されると、このバージョン識別子が引き上げられます。2つのバージョン識別子n1とn2は、n1 == n2であれば同じバージョンを示し、n2 > n1であればn2のバージョンがn1のバージョンより新しいことを示します。
IEvolvableインタフェースは、データの追加による型の進化をサポートするように設計されています。あるデータに前のバージョンの型が依存している場合、そのデータを安全に削除することはできません。同様に、データの前の構造またはセマンティクスに前のバージョンの型が依存している場合、そのデータの構造またはセマンティクスを前のバージョンから安全に変更することはできません。
IEvolvableオブジェクトがデシリアライズされる場合は、新しいバージョンの型に追加された未知のデータ、およびそのデータ形式のバージョン識別子が保持されます。その後、このIEvolvableオブジェクトがシリアライズされると、保持されたバージョン識別子と未知の将来データがオブジェクトに取り込まれます。
古いバージョンを示すバージョン識別子を持つデータ・ストリームからIEvolvableオブジェクトをデシリアライズする場合は、その古いバージョン以降に追加されたデータ・フィールドやプロパティの値をデフォルトに設定するか、または計算する必要があります。その後、このIEvolvableオブジェクトがシリアライズされると、そのバージョン識別子と全データがオブジェクトに取り込まれます。この場合は未知の将来データがないことに注意してください。将来データは、データ・ストリームのバージョンがIEvolvable型のバージョンよりも新しい場合にのみ存在します。
例20-8は、クラスの進化をサポートするように.NET型のContactInfoを変更する方法を示しています。
例20-8 クラスの進化をサポートするようにクラスを変更する
public class ContactInfo : IEvolvablePortableObject
{
private string name;
private string street;
private string city;
private string state;
private string zip;
// IEvolvable members
private int version;
private byte[] data;
public ContactInfo()
{}
public ContactInfo(string name, string street, string city, string state, string zip)
{
Name = name;
Street = street;
City = city;
State = state;
Zip = zip;
}
public void ReadExternal(IPofReader reader)
{
Name = reader.ReadString(0);
Street = reader.ReadString(1);
City = reader.ReadString(2);
State = reader.ReadString(3);
Zip = reader.ReadString(4);
}
public void WriteExternal(IPofWriter writer)
{
writer.WriteString(0, Name);
writer.WriteString(1, Street);
writer.WriteString(2, City);
writer.WriteString(3, State);
writer.WriteString(4, Zip);
}
public int DataVersion
{
get { return version; }
set { version = value; }
}
public byte[] FutureData
{
get { return data; }
set { data = value; }
}
public int ImplVersion
{
get { return 0; }
}
// property definitions ommitted for brevity
}
同様に、Java型のContactInfoは、EvolvablePortableObjectインタフェースを実装することでクラスの進化をサポートするように変更できます。
例20-9 クラスの進化をサポートするようにJava型のクラスを変更する
public class ContactInfo
implements EvolvablePortableObject
{
private String m_sName;
private String m_sStreet;
private String m_sCity;
private String m_sState;
private String m_sZip;
// Evolvable members
private int m_nVersion;
private byte[] m_abData;
public ContactInfo()
{}
public ContactInfo(String sName, String sStreet, String sCity,
String sState, String sZip)
{
setName(sName);
setStreet(sStreet);
setCity(sCity);
setState(sState);
setZip(sZip);
}
public void readExternal(PofReader reader)
throws IOException
{
setName(reader.readString(0));
setStreet(reader.readString(1));
setCity(reader.readString(2));
setState(reader.readString(3));
setZip(reader.readString(4));
}
public void writeExternal(PofWriter writer)
throws IOException
{
writer.writeString(0, getName());
writer.writeString(1, getStreet());
writer.writeString(2, getCity());
writer.writeString(3, getState());
writer.writeString(4, getZip());
}
public int getDataVersion()
{
return m_nVersion;
}
public void setDataVersion(int nVersion) {
m_nVersion = nVersion;
}
public Binary getFutureData()
{
return m_binData;
}
public void setFutureData(Binary binFuture)
{
m_binData = binFuture;
}
public int getImplVersion()
{
return 0;
}
// accessor methods omitted for brevity
}
既存のユーザー定義型を移植可能にする変更が望ましくない場合、または不可能な場合があります。その場合は、IPofSerializerの実装(.NET)またはPofSerializerインタフェースの実装(Java)、あるいはその両方を作成することで、ユーザー定義型の移植可能なシリアライズを外部化できます。
例20-10は、.NET型のContactInfoのIPofSerializerインタフェースの実装を示しています。
例20-10 .NET型のIPofSerializerの実装
public class ContactInfoSerializer : IPofSerializer
{
public object Deserialize(IPofReader reader)
{
string name = reader.ReadString(0);
string street = reader.ReadString(1);
string city = reader.ReadString(2);
string state = reader.ReadString(3);
string zip = reader.ReadString(4);
ContactInfo info = new ContactInfo(name, street, city, state, zip);
info.DataVersion = reader.VersionId;
info.FutureData = reader.ReadRemainder();
return info;
}
public void Serialize(IPofWriter writer, object o)
{
ContactInfo info = (ContactInfo) o;
writer.VersionId = Math.Max(info.DataVersion, info.ImplVersion);
writer.WriteString(0, info.Name);
writer.WriteString(1, info.Street);
writer.WriteString(2, info.City);
writer.WriteString(3, info.State);
writer.WriteString(4, info.Zip);
writer.WriteRemainder(info.FutureData);
}
}
Java型のContactInfoのPofSerializerインタフェースの実装は次のようになります。
例20-11 Java型クラスのPofSerializerの実装
public class ContactInfoSerializer
implements PofSerializer
{
public Object deserialize(PofReader in)
throws IOException
{
String sName = in.readString(0);
String sStreet = in.readString(1);
String sCity = in.readString(2);
String sState = in.readString(3);
String sZip = in.readString(4);
ContactInfo info = new ContactInfo(sName, sStreet, sCity, sState, sZip);
info.setDataVersion(in.getVersionId());
info.setFutureData(in.readRemainder());
return info;
}
public void serialize(PofWriter out, Object o)
throws IOException
{
ContactInfo info = (ContactInfo) o;
out.setVersionId(Math.max(info.getDataVersion(), info.getImplVersion()));
out.writeString(0, info.getName());
out.writeString(1, info.getStreet());
out.writeString(2, info.getCity());
out.writeString(3, info.getState());
out.writeString(4, info.getZip());
out.writeRemainder(info.getFutureData());
}
}
.NET型のContactInfoのIPofSerializer実装を登録するには、POF構成ファイルのContactInfoユーザー定義型に関するuser-type要素で、serializer要素内にIPofSerializerのクラス名を指定します。これを例20-12に示します。
例20-12 .NET型のIPofSerializer実装の登録
<?xml version="1.0"?>
<pof-config xmlns="http://schemas.tangosol.com/pof">
<user-type-list>
<!-- include all "standard" Coherence POF user types -->
<include>assembly://Coherence/Tangosol.Config/coherence-pof-config.xml</include>
<!-- include all application POF user types -->
<user-type>
<type-id>1001</type-id>
<class-name>My.Example.ContactInfo, MyAssembly</class-name>
<serializer>
<class-name>My.Example.ContactInfoSerializer, MyAssembly</class-name>
</serializer>
</user-type>
...
</user-type-list>
</pof-config>
同様に、Java型のContactInfoのPofSerializer実装を登録できます。これを例20-13に示します。
例20-13 Java型のPofSerializer実装の登録
<?xml version="1.0"?>
<!DOCTYPE pof-config SYSTEM "pof-config.dtd">
<pof-config>
<user-type-list>
<!-- include all "standard" Coherence POF user types -->
<include>example-pof-config.xml</include>
<!-- include all application POF user types -->
<user-type>
<type-id>1001</type-id>
<class-name>com.mycompany.example.ContactInfo</class-name>
<serializer>
<class-name>com.mycompany.example.ContactInfoSerializer</class-name>
</serializer>
</user-type>
...
</user-type-list>
</pof-config>