C++クライアントを有効にしてCoherenceクラスタにC++ベースのオブジェクトを正しく格納するには、POF(Portable Object Format)という、プラットフォームに依存しないシリアライズ形式が必要です。POFを使用すると、値オブジェクトのプラットフォームおよび元の言語と関係ない方法で、値オブジェクトをバイナリ・ストリームにエンコードできます。このストリームは、類似するPOFベースのクラス定義を使用して、別の言語でデシリアライズできます。
Coherence C++ APIにはPOFシリアライズ可能クラスがいくつか用意されていますが、カスタムのデータ型では次で説明するようにシリアライズのサポートが必要です。
注意: このドキュメントでは、仕様に基づくクラス定義などの高度な概念も含め、CoherenceのC++オブジェクト・モデルに精通していると想定しています。これらのトピックの詳細は、第10章「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(外部シリアライズ)」を参照してください。
表11-1は、それぞれのアプローチの要件と制限をいくつか示しています。
表11-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>
テンプレートでの使用に適しています。
注意: 見やすさを考慮して、ここではクラス定義の例を宣言の中に記述しています。 |
例11-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で使用できるようになります。この定義のみでは、まだシリアライズに対処できません。例11-2では、シリアライズのサポートが追加されています。
例11-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の知識が必要です。ただし、クラス定義ファイルの中にこのルーチンを記述する必要はなく、 独立したソース・ファイルに記述してもかまいません。最終的なアプリケーションでそのファイルをリンクすると、ルーチンとして正常に機能します。 |
例11-3は、前述の例を使用して、Address
クラスのインスタンスをManaged<T>
でラップしてManaged<Address>
とし、Coherence APIに提供することを示しています。
例11-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.io.Externalizable
に類似しています。coherence::lang::Object
を拡張したクラスであれば、coherence::io::pof::PortableObject
インタフェースを自由に実装してシリアライズのサポートを追加できます。このクラスはObject
を拡張したものであることが必要で、拡張後のクラスのライフサイクルはこのObjectで規定します。
例11-4では、前述のAddress
の例を管理クラスとして書き換え、PortableObject
インタフェースを実装できます。ここでは、このクラスの定義の中でCoherenceオブジェクト・モデル全体を扱うようにしています。たとえば、データ・メンバーに対してstd::string
ではなく、coherence::lang::String
を使用します。
例11-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
例11-5は、Address
の管理バリアントを示しています。このバリアントは、Managed<T>
アダプタの使用を必要とせず、Coherence APIで直接使用できます。
例11-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データ・メンバーを持つ管理オブジェクトを扱う場合です。例11-6では、非PortableObject
バージョンの管理Address
を検討します。
例11-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
には適していません。例11-7は、Address
インスタンスのシリアライズを担当するものとして登録される外部クラスAddressSerializer
を示しています。
例11-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 )のみとします。POF構成ファイルは、オブジェクトをシリアライズおよびデシリアライズするノードでのみ必要です。 |
前述のアプローチのいずれかを完了すると、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
を直接使用することも検討に値します。