C++クライアントを有効にしてCoherenceクラスタにC++ベースのオブジェクトを正しく格納するには、POF(Portable Object Format)という、プラットフォームに依存しないシリアライズ形式が必要です。POFを使用すると、値オブジェクトのプラットフォームおよび元の言語と関係ない方法で、値オブジェクトをバイナリ・ストリームにエンコードできます。このストリームは、類似するPOFベースのクラス定義を使用して、別の言語でデシリアライズできます。
Coherence C++ APIにはPOFシリアライズ可能クラスがいくつか用意されていますが、カスタムのデータ型では次で説明するようにシリアライズのサポートが必要です。
|
注意: このマニュアルでは、仕様に基づくクラス定義などの高度な概念も含め、CoherenceのC++オブジェクト・モデルに精通していると想定しています。これらのトピックの詳細は、第2章「CoherenceのC++オブジェクト・モデルについて」を参照してください。 |
次の型はPOFで内部的にサポートされているので、ユーザー側で特別な処理をする必要はありません。
String
Integer16〜Integer64
Float32、Float64
プリミティブの配列<>
ObjectArray
Boolean
Octet
Character16
また、次の一般的なインタフェースを実装するクラスでは自動POFシリアライズが可能です。
Map
Collection
Exception
Coherence C++ APIが備えているシリアライズ形式は1つのみ(POF)ですが、クラスをシリアライズ可能にするAPIは多数用意されています。どのようなアプローチを採用しても、最終的には同じバイナリPOF形式が生成されます。クラスをシリアライズ可能にするには、次のアプローチを利用できます。
Managed<T>アダプタ・テンプレートを使用して、外部の自由関数シリアライザを追加します。詳細は、「Managed<T>(自由関数シリアライズ)」を参照してください。
データ・オブジェクトを変更してObjectを拡張し、PortableObjectインタフェースを実装して、オブジェクトで自己シリアライズができるようにします。詳細は、「PortableObject(自己シリアライズ)」を参照してください。
データ・オブジェクトを変更してObjectを拡張し、PofSerializerクラスを作成して外部シリアライズを実行します。詳細は、「PofSerializer(外部シリアライズ)」を参照してください。
表3-1は、それぞれのアプローチの要件と制限をいくつか示しています。
表3-1 シリアライズの各オプションの要件と制限
| アプローチ | データ・オブジェクトのCoherenceヘッダー | Objectからの派生の必要性 | constデータ・メンバーのサポート | 外部シリアライズ・ルーチン | 引数がないコンストラクタの必要性 |
|---|---|---|---|---|---|
|
Managed<T> |
なし |
なし |
あり |
あり |
あり |
|
PortableObject |
あり |
あり |
なし |
なし |
あり |
|
PofSerializer |
あり |
あり |
あり |
あり |
なし |
これらのアプローチにはすべて、次のような共通点があります。
データ・アイテムをPOFにエンコードできるようにするシリアライズ・ルーチンを実装する必要があります。
データ・オブジェクトのフィールドを特定するには、数値インデックスを使用します。
データ・オブジェクト・クラスとシリアライズのメカニズムをCoherenceに登録する必要があります。
キャッシュ・キーとして使用するデータ・オブジェクトは、等価比較とハッシュをサポートしている必要があります。
既存のデータ・オブジェクト・クラスのほとんどでは、Managed<T>を使用すると、最も容易にCoherence for C++に統合できます。
非管理クラスがManaged<T>と互換性を持つようにするには、そのクラスに次のような特性があることが必要です。
パラメータのないコンストラクタ(publicまたはprotected): CustomType::CustomType()
コピー・コンストラクタ(publicまたはprotected): CustomType::CustomType(const CustomType&)
等価比較の演算子: ブール演算子==(const CustomType&, const CustomType&)
std::ostream出力関数: std::ostream& operator<<(std::ostream&, const CustomType&)
ハッシュ関数: size_t hash_value(const CustomType&)
次の例は、簡単なAddressクラスを示しています。このクラスは、Coherenceの直接の知識は不要ですが、Managed<T>テンプレートでの使用に適しています。
|
注意: 見やすさを考慮して、ここではクラス定義の例を宣言の中に記述しています。 |
例3-1 非管理クラス
class Address
{
public:
Address(const std::string& sCity, const std::String& sState, int nZip)
: m_sCity(sCity), m_sState(sState), m_nZip(nZip) {}
Address(const Address& that) // required by Managed<T>
: m_sCity(that.m_sCity), m_sState(that.m_sState), m_nZip(that.m_nZip) {}
protected:
Address() // required by Managed<T>
: m_nZip(0) {}
public:
std::string getCity() const {return m_sCity;}
std::string getState() const {return m_sState;}
int getZip() const {return m_nZip;}
private:
const std::string m_sCity;
const std::string m_sState;
const int m_nZip;
};
bool operator==(const Address& addra, const Address& addrb) // required by Managed<T>
{
return addra.getZip() == addrb.getZip() &&
addra.getState() == addrb.getState() &&
addra.getCity() == addrb.getCity();
}
std::ostream& operator<<(std::ostream& out, const Address& addr) // required by Managed<T>
{
out << addr.getCity() << ", " << addr.getState() << " " << addr.getZip();
return out;
}
size_t hash_value(const Address& addr) // required by Managed<T>
{
return (size_t) addr.getZip();
}
Managed<T>と組み合せたこの簡単なクラス定義は、真の管理オブジェクトとなり、Coherence C++ APIで使用できるようになります。この定義のみでは、まだシリアライズに対処できません。例3-2では、シリアライズのサポートが追加されています。
例3-2 シリアライズを使用した管理クラス
#include "coherence/io/pof/SystemPofContext.hpp"
#include "Address.hpp"
using namespace coherence::io::pof;
COH_REGISTER_MANAGED_CLASS(1234, Address); // type ID registration—this must
// appear in the .cpp not the .hpp
template<> void serialize<Address>(PofWriter::Handle hOut, const Address& addr)
{
hOut->writeString(0, addr.getCity());
hOut->writeString(1, addr.getState());
hOut->writeInt32 (2, addr.getZip());
}
template<> Address deserialize<Address>(PofReader::Handle hIn)
{
std::string sCity = hIn->readString(0);
std::string sState = hIn->readString(1);
int nZip = hIn->readInt32 (2);
return Address(sCity, sState, nZip);
}
|
注意: このシリアライズ・ルーチンでは、Coherenceの知識が必要です。ただし、クラス定義ファイルの中にこのルーチンを記述する必要はなく、独立したソース・ファイルに記述してもかまいません。最終的なアプリケーションでそのファイルをリンクすると、ルーチンとして正常に機能します。 |
例3-3は、前述の例を使用して、AddressクラスのインスタンスをManaged<T>でラップしてManaged<Address>とし、Coherence APIに提供することを示しています。
例3-3 Managed<T>でラップしたクラスのインスタンス
// construct the non-managed version as usual
Address office("Redwood Shores", "CA", 94065);
// the managed version can be initialized from the non-managed version
// the result is a new object, which does not reference the original
Managed<Address>::View vOffice = Managed<Address>::create(office);
String::View vKey = "Oracle";
// the managed version is suitable for use with caches
hCache->put(vKey, vAddr);
vOffice = cast<Managed<Address>::View>(hCache->get(vKey));
// the non-managed class's public methods/fields remain accessible
assert(vOffice->getCity() == office.getCity());
assert(vOffice->getState() == office.getState());
assert(vOffice->getZip() == office.getZip());
// conversion back to the non-managed type may be performed using the
// non-managed class's copy constructor.
Address officeOut = *vOffice;
PortableObjectインタフェースは、Java、.NET、C++それぞれのバージョンのCoherenceでサポートされています。このインタフェースは、概念の面から見ると、オブジェクトのシリアライズ方法をそのオブジェクト自身で制御できるようにするjava.io.Externalizableに類似しています。coherence::lang::Objectを拡張したクラスであれば、coherence::io::pof::PortableObjectインタフェースを自由に実装してシリアライズのサポートを追加できます。このクラスはObjectを拡張したものであることが必要で、拡張後のクラスのライフサイクルはこのObjectで規定します。
例3-4では、前述のAddressの例を管理クラスとして書き換え、PortableObjectインタフェースを実装できます。ここでは、このクラスの定義の中でCoherenceオブジェクト・モデル全体を扱うようにしています。たとえば、データ・メンバーに対してstd::stringではなく、coherence::lang::Stringを使用します。
例3-4 PortableObjectを実装する管理クラス
#include "coherence/lang.ns"
#include "coherence/io/pof/PofReader.hpp"
#include "coherence/io/pof/PofWriter.hpp"
#include "coherence/io/pof/PortableObject.hpp"
#include "coherence/io/pof/SystemPofContext.hpp"
using namespace coherence::lang;
using coherence::io::pof::PofReader;
using coherence::io::pof::PofWriter;
class Address
: public cloneable_spec<Address,
extends<Object>,
implements<PortableObject> >
{
friend class factory<Address>;
protected: // constructors
Address(String::View vsCity, String::View vsState, int32_t nZip)
: m_vsCity(self(), vsCity), m_vsState(self(), vsState), m_nZip(nZip) {}
Address(const Address& that)
: super(that), m_vsCity(self(), that.m_vsCity), m_sState(self(),
that.m_vsState), m_nZip(that.m_nZip) {}
Address() // required by PortableObject
: m_nZip(0) {}
public: // Address interface
virtual String::View getCity() const {return m_vsCity;}
virtual String::View getState() const {return m_vsState;}
virtual int32_t getZip() const {return m_nZip;}
public: // PortableObject interface virtual void writeExternal(PofWriter::Handle hOut) const
{
hOut->writeString(0, getCity());
hOut->writeString(1, getState());
hOut->writeInt32 (2, getZip());
}
virtual void readExternal(PofReader::Handle hIn)
{
initialize(m_vsCity, hIn->readString(0));
initialize(m_vsState, hIn->readString(1));
m_nZip = hIn->readInt32 (2);
}
public: // Objectinterface virtual bool equals(Object::View that) const
{
if (instanceof<Address::View>(that))
{
Address::View vThat = cast<Address::View>(that);
return getZip() == vThat->getZip() &&
Object::equals(getState(), vThat->getState()) &&
Object::equals(getCity(), vThat->getCity());
}
return false;
}
virtual size32_t hashCode() const
{
return (size32_t) m_nZip;
}
virtual void toStream(std::ostream& out) const
{
out << getCity() << ", " << getState() << " " << getZip();
}
private:
FinalView<String> m_vsCity;
FinalView<String> m_vsState;
const int32_tm_nZip;
};
COH_REGISTER_PORTABLE_CLASS(1234, Address); // type ID registration—this must
// appear in the .cpp not the .hpp
例3-5は、Addressの管理バリアントを示しています。このバリアントは、Managed<T>アダプタの使用を必要とせず、Coherence APIで直接使用できます。
例3-5 Managed<T>を使用しない管理クラス
Address::View vAddr = Address::create("Redwood Shores", "CA", 94065);
String::View vKey = "Oracle";
hCache->put(vKey, vAddr);
Address::View vOffice = cast<Address::View>(hCache->get(vKey));
Coherenceオブジェクト・モデルを利用してデータ・オブジェクトを記述することにしているアプリケーションでは、PortableObjectを使用したシリアライズが最適です。PortableObjectの短所として、constデータ・メンバーのサポートが容易ではないという点があります。これは、コンストラクタの実行後にreadExternalメソッドがコールされ、その時点でconstデータ・メンバーの値を割り当てる必要があるためです。
3番目のシリアライズ・オプションも最低レベルのものです。PofSerializerは、自身以外のクラスにシリアライズ・ロジックを提供するクラスです。たとえば、前述の管理Addressクラスの非PortableObjectバージョンをシリアライズできるAddressSerializerの例を記述します。前述の2つのアプローチでは、これらは明示的に作成していたのではなく、PofSerializerに委任されていた保護の下で自動的に作成されていました。ほとんどの場合、Managed<T>とPortableObjectのいずれかのアプローチで十分であるため、このアプローチを採用する必要はありません。このアプローチが効果的なのは、constデータ・メンバーを持つ管理オブジェクトを扱う場合です。例3-6では、非PortableObjectバージョンの管理Addressを検討します。
例3-6 非PortableObjectバージョンの管理クラス
#include "coherence/lang.ns"
using namespace coherence::lang;
class Address
: public cloneable_spec<Address> // extends<Object> is implied
{
friend class factory<Address>;
protected: // constructors
Address(String::View vsCity, String::View vsState, int32_t nZip)
: m_vsCity(self(), vsCity), m_vsState(self(), vsState), m_nZip(nZip) {}
Address(const Address& that)
: super(that), m_vsCity(self(), that.m_vsCity), m_sState(self(), that.m_vsState), m_nZip(that.m_nZip) {}
public: // Address interface
virtual String::View getCity() const {return m_vsCity;}
virtual String::View getState() const {return m_vsState;}
virtual int32_t getZip() const {return m_nZip;}
public: // Objectinterface
virtual bool equals(Object::View that) const
{
if (instanceof<Address::View>(that))
{
Address::View vThat = cast<Address::View>(that);
return getZip() == vThat->getZip() &&
Object::equals(getState(), vThat->getState()) &&
Object::equals(getCity(), vThat->getCity());
}
return false;
}
virtual size32_t hashCode() const
{
return (size32_t) m_nZip;
}
virtual void toStream(std::ostream& out) const
{
out << getCity() << ", " << getState() << " " << getZip();
}
private:
const MemberView<String> m_vsCity;
const MemberView<String> m_vsState;
const int32_t m_nZip;
};
このバージョンはconstデータ・メンバーを使用しているため、PortableObjectには適していません。例3-7は、Addressインスタンスのシリアライズを担当するものとして登録される外部クラスAddressSerializerを示しています。
例3-7 シリアライズを担当する外部クラス
#include "coherence/lang.ns"
#include "coherence/io/pof/PofReader.hpp"
#include "coherence/io/pof/PofWriter.hpp"
#include "coherence/io/pof/PortableObject.hpp"
#include "coherence/io/pof/SystemPofContext.hpp"
#include "Address.hpp"
using namespace coherence::lang;
using coherence::io::pof::PofReader;
using coherence::io::pof::PofWriter;
class AddressSerializer
: public class_spec<AddressSerializer,
extends<Object>,
implements<PofSerializer> >
{
friend class factory<AddressSerializer>;
protected:
AddressSerializer();
public: // PofSerializer interface virtual void serialize(PofWriter::Handle hOut, Object::View v) const
{
Address::View vAddr = cast<Address::View>(v);
hOut->writeString(0, vAddr->getCity());
hOut->writeString(1, vAddr->getState());
hOut->writeInt32 (2, vAddr->getZip());
hOut->writeRemainder(NULL);
}
virtual Object::Holder deserialize(PofReader::Handle hIn) const
{
String::View vsCity = hIn->readString(0);
String::View vsState = hIn->readString(1);
int32_t nZip = hIn->readInt32 (2);
hIn->readRemainder();
return Address::create(vsCity, vsState, nZip);
}
};
COH_REGISTER_POF_SERIALIZER(1234, TypedBarrenClass<Address>::create(),AddressSerializer::create()); // This must appear in the .cpp not the .hpp
Addressの使用方法は、次のようにこれまでと同じです。
Address::View vAddr = Address::create("Redwood Shores", "CA", 94065);
String::View vKey = "Oracle";
hCache->put(vKey, vAddr);
Address::View vOffice = cast<Address::View>(hCache->get(vKey));
シリアライズ可能にすることに加え、各クラスには数値型のIDを関連付ける必要があります。これらのIDはクラスタ全体で既知の状態になっています。クラスタでは、POFユーザー定義型の構成要素を使用して、IDとクラスとのマッピングを構成します。C++では、IDの登録の形式でクラス定義の中にこのマッピングを埋め込みます。これは、クラスの.cppソース・ファイルに記述します。
この登録の手法は、次のようにシリアライズのアプローチごとに少々異なります。
COH_REGISTER_MANAGED_CLASS(ID, TYPE): Managed<T>による方法の場合
COH_REGISTER_PORTABLE_CLASS(ID, TYPE): PortableObjectによる方法の場合
COH_REGISTER_POF_SERIALIZER(ID, CLASS, SERIALIZER): PofSerializerによる方法の場合
これらの登録の例は、前述の例に記載されています。
|
注意: 登録を記述するファイルは実装ファイル(.cpp)のみとします。 |
前述のアプローチのいずれかを完了すると、Coherenceクラスタにデータ・オブジェクトを格納できるようになります。これにより、オブジェクトに対してgetベースとputベースの操作を実行できます。ただし、問合せや入力プロセッサなど、Coherenceのさらに高度な機能を利用する場合は、なんらかのJavaコードを記述する必要があります。このような高度な機能が動作するためには、CoherenceのJavaベース・キャッシュ・サーバーは、単にデータ・オブジェクトのシリアライズした表現を保持するのみでなく、データ・オブジェクトと対話できる必要があります。データ・オブジェクトと対話し、そのプロパティにアクセスするには、キャッシュ・サーバーでJavaのいずれかのバージョンを利用できる必要があります。POF上でJavaのバージョンをシリアライズ可能にするためのアプローチは、前述の例とほとんど同じです。詳細は、com.tangosol.io.pof.PortableObjectおよびcom.tangosol.io.pof.PofSerializerを参照してください。どちらもC++ベースの3種類のアプローチで利用できます。
Managed<T>とPortableObjectは、いずれもバックグラウンドでPofSerializerを使用してシリアライズを実行します。これらのアプローチのそれぞれで独自のオーバーヘッドが発生します。たとえば、Managed<T>によるアプローチでは、デシリアライズの過程で、一時バージョンのデータ・オブジェクトが非管理形式で作成されます。PortableObjectの場合は、constデータ・メンバーに対するサポートがないことから、constデータ・メンバーに対して可能な最適化が回避されるために、余分なコストが発生する可能性があります。全体としてはパフォーマンスの差は無視できますが、可能なかぎり最大のパフォーマンス実現を追求する場合は、PofSerializerを直接使用することも検討に値します。