キャッシュのエンコードとマーシャリング

Red Hat Data Grid 8.3

Data Grid キャッシュのエンコードと Java オブジェクトのマーシャリング

概要

Data Grid キャッシュは、異なるエンコーディングにキーと値を保存できます。本書では、Data Grid がリモートおよび埋め込みキャッシュのデータをエンコードする方法と、アプリケーションでさまざまなメディアタイプを使用する方法について説明します。さらに、ProtoStream API の Data Grid 実装を使用して、Java オブジェクトを Protocol Buffers(Protobuf) としてマーシャリングする方法を説明します。ProtoStream アノテーションを Java クラスに追加し、マーシャリング機能およびインデックス化されたクエリー用に Protobuf スキーマを生成する詳細についても説明します。

Red Hat Data Grid

Data Grid は、高性能の分散型インメモリーデータストアです。

スキーマレスデータ構造
さまざまなオブジェクトをキーと値のペアとして格納する柔軟性があります。
グリッドベースのデータストレージ
クラスター間でデータを分散および複製するように設計されています。
エラスティックスケーリング
サービスを中断することなく、ノードの数を動的に調整して要件を満たします。
データの相互運用性
さまざまなエンドポイントからグリッド内のデータを保存、取得、およびクエリーします。

Data Grid のドキュメント

Data Grid のドキュメントは、Red Hat カスタマーポータルで入手できます。

Data Grid のダウンロード

Red Hat カスタマーポータルで Data Grid Software Downloads にアクセスします。

注記

Data Grid ソフトウェアにアクセスしてダウンロードするには、Red Hat アカウントが必要です。

多様性を受け入れるオープンソースの強化

Red Hat では、コード、ドキュメント、Web プロパティーにおける配慮に欠ける用語の置き換えに取り組んでいます。まずは、マスター (master)、スレーブ (slave)、ブラックリスト (blacklist)、ホワイトリスト (whitelist) の 4 つの用語の置き換えから始めます。この取り組みは膨大な作業を要するため、今後の複数のリリースで段階的に用語の置き換えを実施して参ります。詳細は、Red Hat CTO である Chris Wright のメッセージ をご覧ください。

第1章 キャッシュエンコーディングの設定

さまざまなメディアタイプで Data Grid キャッシュを設定する方法と、エンコーディングが Data Grid を使用する方法に与える影響について説明します。

1.1. キャッシュエンコーディング

エンコーディングは、メディアタイプで識別される形式です。これは、Data Grid がエントリー (キー/値のペア) をキャッシュに格納するために使用する形式です。

リモートキャッシュ

Data Grid Server は、キャッシュ設定に設定されるエンコーディングを使用してリモートキャッシュにエントリーを保存します。

Hot Rod および REST クライアントには、Data Grid Server に対して行う各要求を持つメディアタイプが含まれます。異なるメディアタイプで読み書き要求を行う複数のクライアントを処理するには、Data Grid Server は、データをオンデマンドで、キャッシュ設定で設定されているメディアタイプとの間で変換します。

リモートキャッシュにエンコーディング設定がない場合、Data Grid Server はメディアタイプの情報なしにキーおよび値を汎用 byte[] として保存します。これにより、異なる形式を要求するクライアントのデータを変換する際に予期せぬ結果が生じる可能性があります。

ProtoStream エンコーディングの使用

Data Grid Server は、クライアントの要求に、キャッシュ設定で設定されるメディアタイプとの間で変換できないメディアタイプが含まれる場合に、エラーを返します。

Data Grid は、Data Grid Console、CLI、Hot Rod、REST などの複数のクライアントを使用する場合は、application/x-protostream メディアタイプでキャッシュエンコーディングを常に設定することを推奨しています。ProtoStream エンコーディングでは、サーバー側のタスクを使用し、リモートキャッシュでインデックス化されたクエリーを実行することもできます。

組み込みキャッシュ

Data Grid は、デフォルトで Plain Old Java Objects(POJO) として埋め込みキャッシュにエントリーを保存します。

クラスター化された組み込みキャッシュの場合、Data Grid はすべての POJO をノード間で複製し、その後に POJO にアンマーシャリングできるバイトアレイにマーシャリングする必要があります。つまり、別のマーシャラーを設定しない場合は、Data Grid が ProtoStream マーシャラーで POJO をシリアライズできるようにする必要があります。

注記

ミュータブルな POJO を組み込みキャッシュに保存する場合は、常に新しい POJO インスタンスを使用して値を更新する必要があります。たとえば、HashMap をキー/値のペアとして保存すると、Data Grid クラスターの他のメンバーには、マップへのローカル変更は表示されません。さらに、Data Grid がオブジェクトをマーシャリングするのと当時にマップインスタンスが更新されると、ConcurrentModificationException が発生する可能性があります。

1.2. Protobuf キャッシュエンコーディング

Protocol Buffers(Protobuf) は、構造化されたデータの軽量バイナリーメディアタイプです。キャッシュエンコーディングとして、Protobuf は Hot Rod および REST エンドポイントの両方について、異なるプログラミング言語でのクライアントアプリケーション間で優れたパフォーマンスと相互運用性を提供します。

Data Grid は ProtoStream ライブラリーを使用して、キャッシュを application/x-protostream メディアタイプで Protobuf としてエンコードします。

以下の例は、Person オブジェクトを記述する Protobuf メッセージを示しています。

message Person {
    optional int32 id = 1;
    optional string name = 2;
    optional string surname = 3;
    optional Address address = 4;
    repeated PhoneNumber phoneNumbers = 5;
    optional uint32 age = 6;
    enum Gender {
        MALE = 0;
        FEMALE = 1;
    }
}

相互運用性

Protobuf エンコーディングは言語に依存しないことから、Data Grid は Java、C++、C#、Python、Go などで書かれたクライアントアプリケーションからのリクエストを処理できます。

Protobuf を使用すると、異なるリモートエンドポイントのクライアント (Hot Rod または REST) が同じデータで操作できます。REST API を使用するため、Data Grid Console を使用して Protobuf でエンコードされたキャッシュにアクセスして作業できます。

注記

application/x-protostream 以外のバイナリーエンコーディングで Data Grid Console を使用することはできません。

アプリケーションとサービス間の通信を可能にするため、すべての Red Hat テクノロジーとの統合には、常に Protobuf キャッシュエンコーディングを application/x-protostream メディアタイプと共に使用する必要があります。

クエリー

Data Grid には、高速で信頼できるクエリーのために、キャッシュ内のデータの構造化表現が必要です。Ickle クエリー言語でキャッシュを検索するには、オブジェクトを記述する Protobuf スキーマを登録します。

カスタムタイプ

Data Grid には、String および Integer を含む頻繁に使用されるタイプに対するネイティブサポートを持つ ProtoStream API の実装が含まれています。カスタムタイプをキャッシュに保存する場合は、ProtoStream マーシャリングを使用して、シリアル化コンテキストを生成して Data Grid で登録し、オブジェクトをマーシャリングできるようにします。

1.2.1. ProtoStream としてのキャッシュのエンコード

ProtoStream ライブラリーを使用して、キャッシュエントリーを Protocol Buffers(Protobuf) として保存するように Data Grid を設定します。

手順

  • キーおよび値の application/x-protostream メディアタイプを指定します。

宣言的 (Declarative)

<distributed-cache>
   <encoding>
      <key media-type="application/x-protostream"/>
      <value media-type="application/x-protostream"/>
   </encoding>
</distributed-cache>

プログラマティック

//Create cache configuration that encodes keys and values as ProtoStream
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.clustering().cacheMode(CacheMode.DIST_SYNC)
       .encoding().key().mediaType("application/x-protostream")
       .encoding().value().mediaType("application/x-protostream");

または、キーと値に同じエンコーディングを使用できます。

宣言的 (Declarative)

<encoding media-type="application/x-protostream"/>

プログラマティック

.encoding().mediaType("application/x-protostream");

1.3. テキストベースのキャッシュエンコーディング

テキストベースのエンコーディングは、プレーンテキストなどの人間が判読できるコンテンツです。従来の "Hello World" のサンプルエントリーは、以下のようにキャッシュに保存できます。

key=hello
value=world

text/plain メディアタイプでキャッシュをエンコードする場合、Data Grid は以下のメディアタイプ間で変換できます。

  • application/xml
  • application/json
  • application/x-protostream

以下の設定例では、text/plain; charset=UTF-8 メディアタイプでキーと値をエンコードします。

<distributed-cache>
   <encoding>
      <key media-type="text/plain; charset=UTF-8"/>
      <value media-type="text/plain; charset=UTF-8"/>
   </encoding>
</distributed-cache>

1.3.1. クライアントおよびテキストベースのエンコーディング

テキストベースのメディアタイプで鍵と値を格納するようにエンコーディングを設定する場合、これらのキャッシュで動作するようにクライアントを設定する必要もあります。

Hot Rod クライアント

Data Grid は ProtoStream ライブラリーを使用して、String String および byte[] タイプをネイティブに処理します。text/plain メディアタイプでキャッシュエンコーディングを設定する場合、Hot Rod クライアントはキャッシュ操作を実行するためのマーシャラー設定を必要とするとは限りません。

JSON や XML などの他のテキストベースのメディアタイプでは、Hot Rod クライアントは、text/plain メディアタイプで変換する org.infinispan.commons.marshall.UTF8StringMarshaller マーシャラーを使用できます。

REST クライアント

REST クライアントには、要求ヘッダーにキャッシュのメディアタイプを含める必要があります。

たとえば、キャッシュエンコーディングを text/plain; charset=UTF-8 として設定した場合、REST クライアントは以下のヘッダーを送信します。

  • Accept: text/plain; charset=UTF-8(読み取り操作の場合)
  • Content-Type: text/plain; charset=UTF-8 または Key-Content-Type: text/plain; charset=UTF-8(書き込み操作の場合)

1.4. マーシャリングされた Java オブジェクト

Data Grid は、マーシャリングされた Java オブジェクトをバイトアレイとしてキャッシュに保存します。たとえば、以下は、メモリーに値として保存される Person オブジェクトの単純な表現です。

value=[61 6c 61 6e 0a 70 61 72 74 72 69 64 67 65]

キャッシュにマーシャリングされたオブジェクトを保存するには、厳密な要件がない限り、ProtoStream マーシャラーを使用する必要があります。たとえば、クライアントアプリケーションを古いバージョンの Data Grid から移行する場合は、Hot Rod Java クライアントで JBoss マーシャリングを一時的に使用する必要がある場合があります。

Data Grid は、以下のメディアタイプでマーシャリングされた Java オブジェクトをバイトアレイとして保存します。

  • application/x-protostream
  • application/x-jboss-marshalling
  • application/x-java-serialized-object
注記

アンマーシャリングされた Java オブジェクトを保存する場合、Data Grid は equals() および hashCode() のオブジェクト実装を使用します。マーシャリングされたオブジェクトを保存する場合、マーシャリングされたバイトは等価性について比較され、代わりにハッシュ化されます。

1.4.1. クライアントおよびマーシャリングされたオブジェクト

Hot Rod Java クライアントをマーシャラーを使用するように設定する場合は、そのマーシャラーのエンコーディングでキャッシュを設定する必要があります。

それぞれのマーシャラーは異なるメディアタイプを使用して、クライアントが Data Grid Server に送信できる byte[] コンテンツを生成します。サーバーから読み取る場合、クライアントマーシャラーは逆の操作を行います。メディアタイプを使用して byte[] コンテンツからデータを生成します。

キャッシュエンコーディングは Hot Rod クライアントマーシャラーと互換性がある必要があります。たとえば、キャッシュエンコーディングを application/x-protostream として設定している場合、クライアント上で ProtoStream マーシャラーを使用して、そのキャッシュ上で動作させることができます。ただし、クライアントマーシャラーが、Data Grid が application/x-protostream との間で変換できないエンコーディングを使用する場合、Data Grid はエラーメッセージを出力します。

JavaSerializationMarshaller または GenericJBossMarshaller を使用する場合は、キャッシュを application/x-java-serialized-object または application/x-jboss-marshalling メディアタイプでエンコードする必要があります。

ProtoStream から JSON への変換

Data Grid は、application/x-protostream メディアタイプでエンコードされたキーと値を application/json に変換します。

これにより、REST クライアントは要求ヘッダーに JSON メディアタイプを追加し、ProtoStream エンコーディングを使用するキャッシュで操作を実行することができます。

  • Accept: application/json(読み取り操作の場合)
  • Content-Type: application/json(書き込み操作の場合)

1.5. Plain Old Java Objects (POJO)

最適なパフォーマンスを得るには、Data Grid は、埋め込みキャッシュにのみアンマーシャリングされた POJO を保存することを推奨します。ただし、以下のメディアタイプを使用してキーと値を設定できます。

  • application/x-java-object

1.5.1. クライアントおよび POJO

Data Grid はこれを推奨しませんが、クライアントは application/x-java-object メディアタイプでアンマーシャリングされた POJO を格納するキャッシュで操作できます。

Hot Rod クライアント

Hot Rod クライアントマーシャラーは、Java オブジェクトをデシリアライズできるように Data Grid Server で利用できる必要があります。デフォルトでは、ProtoStream および Java Serialization マーシャラーはサーバー上で利用できます。

REST クライアント

REST クライアントは、キーと値に JSON または XML を使用し、Data Grid が POJO との間で変換できるようにします。

注記

Data Grid では、XML を POJO との間で変換するためにデシリアライズ許可リストに Java クラスを追加する必要があります。

1.6. JAR の Data Grid Server のインストールへの追加

カスタム JAR ファイルをクラスパスに追加して、Data Grid Server でカスタム JAR ファイルを利用できるようにします。

重要
  • Data Grid は、起動時にのみ JAR ファイルをロードします。

    クラスターのすべてのノードを正常に停止し、クラスターを起動する前に、各ノードで JAR ファイルを利用できるようにする必要があります。

  • カスタム JAR ファイルは $RHDG_HOME/server/lib ディレクトリーにのみ追加する必要があります。

    $RHDG_HOME/lib ディレクトリーは、Data Grid JAR ファイル用に予約されています。

手順

  1. Data Grid Server が実行している場合は停止します。
  2. JAR ファイルを server/lib ディレクトリーに追加します。以下に例を示します。

    ├── server
    │   └── lib
    │       └── UserObjects.jar

第2章 ProtoStream を使用したカスタムオブジェクトのマーシャリング

マーシャリングは、Java オブジェクトをバイナリー形式に変換して、ネットワーク全体で転送したり、ディスクに格納したりできるプロセスです。逆プロセスのアンマーシャリングは、データをバイナリー形式から Java オブジェクトに変換します。

Data Grid は、マーシャリングとアンマーシャリングを実行し、以下を行います。

  • クラスターの他の Data Grid ノードにデータを送信します。
  • データを永続キャッシュストアに保存します。
  • クライアントとリモートキャッシュ間でオブジェクトを送信します。
  • JVM ヒープ外にあるネイティブメモリーにオブジェクトを保存します。
  • キャッシュエンコーディングが application/x-java-object ではない場合に、オブジェクトを JVM ヒープメモリーに保存します。

Data Grid キャッシュにカスタムオブジェクトを保存する場合は、ProtoStream マーシャラーで Protobuf ベースのマーシャリングを使用する必要があります。

2.1. ProtoStream マーシャリング

Data Grid は ProtoStream API を提供するため、Java オブジェクトを Protocol Buffers(Protobuf) としてマーシャリングできます。

ProtoStream は、多くの異なる Java データ型をネイティブにサポートします。つまり、これらのタイプに ProtoStream マーシャリングを設定する必要はありません。カスタムまたはユーザータイプの場合、Data Grid が、これらのオブジェクトをキャッシュとの間でマーシャリングできるように情報を提供する必要があります。

SerializationContext
Protobuf スキーマ (.proto ファイル) から読み込まれる Protobuf タイプの定義が含まれるリポジトリー、および付随マーシャラー。
SerializationContextInitializer
SerializationContext を初期化するインターフェイス。

2.1.1. ProtoStream タイプ

Data Grid は、キーと値の以下の型、およびプリミティブタイプの場合にボックスのない同等を処理できる ProtoStream ライブラリーを使用します。

  • byte[]
  • Byte
  • 文字列
  • 整数
  • Long
  • double
  • Float
  • ブール値
  • Short
  • Character
  • java.util.Date
  • java.time.Instant
追加のタイプコレクション

ProtoStream ライブラリーには、一般的な Java タイプ用のアダプタークラスが複数含まれます。以下に例を示します。

  • java.math.BigDecimal
  • java.math.BigInteger
  • java.util.UUID

Data Grid は、protostream-types アーティファクトにある一部の一般的な JDK クラスのすべてのアダプタークラスを提供します。これは、infinispan-core および infinispan-client-hotrod 依存関係に含まれます。アダプタークラスをキーまたは値として保存する設定は必要ありません。

しかし、アダプタークラスを ProtoStream annotated POJOS でマーシャリング可能なフィールドとして使用する場合は、以下の方法で実行できます。

  • AutoProtoSchemaBuilder アノテーションの dependsOn 要素を使用して、CommonTypesSchema および CommonContainerTypesSchema クラスを指定します。
@AutoProtoSchemaBuilder(dependsOn = {org.infinispan.protostream.types.java.CommonTypes, org.infinispan.protostream.types.java.CommonContainerTypes}, schemaFileName = "library.proto", schemaFilePath = "proto", schemaPackageName = "example")
public interface LibraryInitalizer extends SerializationContextInitializer {
}
  • AutoProtoSchemaBuilder アノテーションの includeClasses 要素で必要なアダプタークラスを指定します。
@AutoProtoSchemaBuilder(includeClasses = { Author.class, Book.class, UUIDAdaptor.class, java.math.BigInteger }, schemaFileName = "library.proto", schemaFilePath = "proto", schemaPackageName = "library")
public interface LibraryInitalizer extends SerializationContextInitializer {

}

2.1.2. ProtoStream アノテーション

ProtoStream API には、Protobuf スキーマを定義するために Java アプリケーションに追加できるアノテーションが含まれます。これにより、オブジェクトに構造化された形式が提供されます。

このトピックでは、ProtoStream アノテーションに関する追加情報を提供します。詳細は、org.infinispan.protostream.annotations パッケージのドキュメントを参照してください。

ProtoField

@ProtoField は Protobuf message フィールドを定義します。

このアノテーションは必須で、getter メソッドおよび setter メソッドにくわえてフィールドにも適用されます。Data Grid が Protobuf としてマーシャリングする前に、クラスに @ProtoField アノテーションが付けられたフィールドを 1 つ以上設定する必要があります。

パラメーター任意または必須説明

number

整数

必須

タグ番号はクラス内で一意である必要があります。

type

任意

フィールドの Protobuf タイプを宣言します。タイプを指定しない場合は、Java プロパティーから推測されます。

Java intfixed32 に変更する場合と同様に、@ProtoField(type) 要素を使用して Protobuf タイプを変更できます。Java プロパティーに互換性のない宣言を使用すると、コンパイラーエラーが発生します。

collectionImplementation

クラス

任意

プロパティータイプがインターフェイスまたは抽象クラスである場合に、実際のコレクションタイプを示します。

javaType

クラス

任意

プロパティータイプが抽象クラスまたはインターフェイスである場合に、実際の Java タイプを示します。この値は、プロパティータイプに対して割り当て可能なインスタンス化可能な Java タイプである必要があります。

javaType パラメーターでタイプを宣言する場合、すべてのユーザーコードはそのタイプに準拠する必要があります。エントリー用に生成されたマーシャラーは、アンマーシャリングの場合にその実装を使用します。ローカルクライアントが宣言とは異なる実装を使用する場合は、ClassCastExceptions が発生します。

name

文字列

任意

Protobuf スキーマの名前を指定します。

defaultValue

文字列

任意

キャッシュからの読み取り時に利用できない場合は、フィールドのデフォルト値を指定します。値は、Java フィールドタイプの正しい構文に従う必要があります。

ProtoFactory

@ProtoFactory は、メッセージクラスのインスタンスを作成するための単一のコンストラクターまたは静的ファクトリーメソッドをマークします。

このアノテーションを使用してイミュータブルメッセージクラスをサポートすることができます。@ProtoField アノテーションが付けられたフィールドはすべてパラメーターに追加する必要があります。

  • @ProtoFactory コンストラクターまたはメソッドのフィールド名およびパラメーターは、対応する Protobuf メッセージと一致する必要がありますが、順番は重要ではありません。
  • @ProtoFactory アノテーションが付けられたコンストラクターをクラスに追加しないと、そのクラスにはデフォルトの no-argument コンストラクターが必要です。そうしないと、コンパイル中にエラーが発生します。
AutoProtoSchemaBuilder

@AutoProtoSchemaBuilder は、SerializationContextInitializer を拡張するクラスまたはインターフェイスの実装を生成します。

アクティブの場合、ProtoStream プロセッサーは、Impl 接尾辞または className パラメーターで指定した名前と同じパッケージで、コンパイル時に実装を生成します。

includeClasses または basePackages パラメーターは、ProtoStream プロセッサーがスキャンして Protobuf スキーマおよびマーシャラーに含める必要があるクラスを参照します。これらのパラメーターのいずれかを設定しないと、ProtoStream プロセッサーはソースパス全体をスキャンします。これにより、予期しない結果が発生する可能性があり、推奨されません。basePackages パラメーターと excludeClasses パラメーターを使用して、クラスを除外することもできます。

schemaFileName パラメーターおよび schemaPackageName パラメーターは、この名前で生成された Protobuf スキーマを登録します。これらのパラメーターを設定しないと、アノテーション付きの単純なクラス名が名前なしまたは デフォルトパッケージで使用されます。スキーマ名は、.proto ファイル拡張子で終了する必要があります。marshallersOnly を使用してマーシャラーのみを生成し、Protobuf スキーマの生成を抑制することもできます。

ProtoStream プロセスは、META-INF/services サービスメタデータファイルを自動的に生成します。このファイルを使用すると、Data Grid Server が JAR を自動的に検出して Protobuf スキーマを登録します。

dependsOn パラメーターは、最初に実行するように SerializedContextInitializer を実装するアノテーション付きクラスを一覧表示します。クラスが SerializedContextInitializer を実装していないか、AutoProtoSchemaBuilder のアノテーションが付けられた場合は、コンパイル時間エラーが発生します。

ProtoAdapter

@ProtoAdapter は、直接アノテーションを付けることができないクラスまたは列挙のマーシャリングアダプターです。

以下の項目に、このアノテーションを使用する場合、

  • クラス: アノテーションが付けられたクラスには、マーシャリングされたクラス用に @ProtoFactory アノテーションが付けられたファクトリーメソッド、および各フィールド用にアノテーション付きアクセサーメソッドが必要です。これらのメソッドはインスタンスまたは静的なメソッドにすることができます。また、最初の引数はマーシャリングされたクラスである必要があります。
  • 列挙: 同じ名前の列挙値が、ターゲット列挙に存在している必要があります。
ProtoDoc および ProtoDocs

@ProtoDoc および @ProtoDocs は、生成されたスキーマのメッセージタイプ、列挙、またはフィールドを文書化する人間が判読できるテキストです。

これらのアノテーションを使用して、Ickle クエリーのインデックスを設定します。

ProtoName

@protoName は、Protobuf メッセージまたは列挙型名を指定し、@ProtoMessage アノテーションを置き換えるオプションのアノテーションです。

ProtoEnumValue

@ProtoEnumValue は Protobuf 列挙値を定義します。このアノテーションは、Java enum のメンバーにのみ適用できます。

ProtoReserved および ProtoReservedStatements

@ProtoReserved および @ProtoReservedStatements は、生成されたメッセージまたは列挙定義に reserved ステートメントを追加し、番号、範囲、および名前の使用を防ぎます。

ProtoTypeId

@ProtoTypeId は任意で、Protobuf メッセージまたは enum タイプのグローバル一意の数値型識別子を指定します。

注記

Data Grid は内部的にこのアノテーションを使用し、識別子は通知なしに変更できるため、このアノテーションをクラスに追加しないでください。

ProtoUnknownFieldSet

@ProtoUnknownFieldSet はオプションで、未知のフィールドを保存するタイプ {@link org.infinispan.protostream.UnknownFieldSet} のフィールドまたは JavaBean プロパティーを示します。

注記

]Data Grid は、Google でサポートされなくなり、今後削除される可能性があるため、このアノテーションの使用は推奨していません。

2.2. シリアル化のコンテキストイニシャライザーの作成

シリアル化コンテキストイニシャライザーを使用すると、Data Grid で以下を登録することができます。

  • ユーザータイプを記述する Protobuf スキーマ。
  • シリアル化およびデシリアライズ機能を提供するマーシャラー。

概略としては、以下の操作を行いシリアル化コンテキストイニシャライザーを作成する必要があります。

  1. ProtoStream アノテーションを Java クラスに追加します。
  2. Data Grid が提供する ProtoStream プロセッサーを使用して、SerializationContextInitializer 実装をコンパイルします。
注記

org.infinispan.protostream.MessageMarshaller インターフェイスは非推奨となり、ProtoStream の今後のバージョンで削除される予定です。完全に削除されるまで MessageMarshaller の使用方法を示すコード例またはドキュメントは無視する必要があります。

2.2.1. ProtoStream プロセッサーの追加

Data Grid は、コンパイル時にクラスの Java アノテーションを処理する ProtoStream プロセッサーアーティファクトを提供し、Protobuf スキーマ、付随マーシャラー、および SerializationContextInitializer インターフェイスの具体的な実装を生成します。

手順

  • provided スコープで protostream-processor 依存関係を pom.xml に追加します。

    注記

    この依存関係はコンパイル時にのみ必要です。そのため、provided スコープを使用するか、オプションとしてマークする必要があります。また、protostream-processor が推移的な依存関係として伝播されないようにする必要があります。

    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.infinispan</groupId>
          <artifactId>infinispan-bom</artifactId>
          <version>${version.infinispan}</version>
          <type>pom</type>
        </dependency>
      </dependencies>
    </dependencyManagement>
    
    <dependencies>
      <dependency>
        <groupId>org.infinispan.protostream</groupId>
        <artifactId>protostream-processor</artifactId>
        <scope>provided</scope>
      </dependency>
    </dependencies>

2.2.2. ProtoStream アノテーションの Java クラスへの追加

Java クラスとそのメンバーにアノテーションを追加して ProtoStream メタデータを宣言します。次に、Data Grid は ProtoStream プロセッサーを使用して Protobuf スキーマと、それらのアノテーションから関連するマーシャラーを生成します。

手順

  1. フィールドで直接、または getter メソッドまたは setter メソッドのいずれかで、@ProtoField でマーシャリングする Java フィールドにアノテーションを付けます。

    Java クラスのアノテーションが付けられていないフィールドは一時的です。たとえば、Java クラスに 15 つのフィールドがあり、それらのうち 5 つにアノテーションを付けるとします。作成されるスキーマにはこれらの 5 つのフィールドのみが含まれ、これらの 5 つのフィールドのみが Data Grid にクラスインスタンスを保存するときにマーシャリングされます。

  2. @ProtoFactory を使用して、イミュータブルオブジェクトのコンストラクターにアノテーションを付けます。アノテーションが付けられたコンストラクターは @ProtoField アノテーションが付けられたフィールドをすべて初期化する必要があります。
  3. @ProtoEnumValue で Java 列挙型のメンバーにアノテーションを付けます。

以下の Author.java および Book.java の例は、@ProtoField および @ProtoFactory のアノテーションが付けられた Java クラスを示しています。

Author.java

import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;

public class Author {
   @ProtoField(1)
   final String name;

   @ProtoField(2)
   final String surname;

   @ProtoFactory
   Author(String name, String surname) {
      this.name = name;
      this.surname = surname;
   }
   // public Getter methods omitted for brevity
}

Book.java

import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;

public class Book {
   @ProtoField(number = 1)
   public final UUID id;

   @ProtoField(number = 2)
   final String title;

   @ProtoField(number = 3)
   final String description;

   @ProtoField(number = 4, defaultValue = "0")
   final int publicationYear;

   @ProtoField(number = 5, collectionImplementation = ArrayList.class)
   final List<Author> authors;

   @ProtoField(number = 6)
   public Language language;

   @ProtoFactory
   Book(UUID id, String title, String description, int publicationYear, List<Author> authors, Language language) {
      this.id = id;
      this.title = title;
      this.description = description;
      this.publicationYear = publicationYear;
      this.authors = authors;
      this.language = language;
   }
   // public Getter methods not included for brevity
}

以下の Language.java の例は、対応する Protobuf スキーマとともに @ProtoEnumValue アノテーションが付けられた Java 列挙を示しています。

Language.java

import org.infinispan.protostream.annotations.ProtoEnumValue;

public enum Language {
  @ProtoEnumValue(number = 0, name = "EN")
  ENGLISH,
  @ProtoEnumValue(number = 1, name = "DE")
  GERMAN,
  @ProtoEnumValue(number = 2, name = "IT")
  ITALIAN,
  @ProtoEnumValue(number = 3, name = "ES")
  SPANISH,
  @ProtoEnumValue(number = 4, name = "FR")
  FRENCH;

}

Language.proto

enum Language {

   EN = 0;

   DE = 1;

   IT = 2;

   ES = 3;

   FR = 4;
}

2.2.3. ProtoStream アダプタークラスの作成

ProtoStream は @ProtoAdapter アノテーションを提供し、これを使用して、直接アノテーションを付けることができない外部のサードパーティーの Java オブジェクトクラスをマーシャリングすることができます。

手順

  1. Adapter クラスを作成し、以下の例のように @ProtoAdapter アノテーションを追加します。

    import java.util.UUID;
    
    import org.infinispan.protostream.annotations.ProtoAdapter;
    import org.infinispan.protostream.annotations.ProtoFactory;
    import org.infinispan.protostream.annotations.ProtoField;
    import org.infinispan.protostream.descriptors.Type;
    
    /**
     * Human readable UUID adapter for UUID marshalling
     */
    @ProtoAdapter(UUID.class)
    public class UUIDAdapter {
    
      @ProtoFactory
      UUID create(String stringUUID) {
        return UUID.fromString(stringUUID);
      }
    
      @ProtoField(1)
      String getStringUUID(UUID uuid) {
        return uuid.toString();
      }
    }

2.2.4. シリアル化コンテキストイニシャライザーの生成

ProtoStream プロセッサーを追加して Java クラスにアノテーションを付けると、Data Grid が Protobuf スキーマ、付随するマーシャラー、および SerializationContextInitializer の具体的な実装を生成するように @AutoProtoSchemaBuilder アノテーションをインターフェイスに追加できます。

注記

デフォルトでは、生成された実装名は Impl 接尾辞が付いたアノテーション付きクラス名です。

手順

  1. GeneratedSchema を拡張するインターフェイスまたはそのスーパーインターフェイス SerializationContextInitializer を定義します。

    注記

    GeneratedSchema インターフェイスには Protobuf スキーマにアクセスするためのメソッドが含まれていますが、SerializationContextInitializer インターフェイスは登録メソッドのみをサポートします。

  2. インターフェイスに @AutoProtoSchemaBuilder アノテーションを付けます。
  3. includeClasses パラメーターには、生成された SerializationContextInitializer 実装のすべてのクラスが含まれることを確認します。
  4. schemaFileName パラメーターで、生成された .proto スキーマの名前を指定します。
  5. schemaFilePath パラメーターで、スキーマファイルが生成される target/classes の下にパスを設定します。
  6. schemaPackageName パラメーターで、生成された .proto スキーマのパッケージ名を指定します。

以下の例は、@AutoProtoSchemaBuilder アノテーションが付けられた GeneratedSchema インターフェイスを示しています。

@AutoProtoSchemaBuilder(
      includeClasses = {
            Book.class,
            Author.class,
            UUIDAdapter.class,
            Language.class
      },
      schemaFileName = "library.proto",
      schemaFilePath = "proto/",
      schemaPackageName = "book_sample")
interface LibraryInitializer extends GeneratedSchema {
}

次のステップ

埋め込みキャッシュを使用する場合は、Data Grid は SerializationContextInitializer 実装を自動的に登録します。

リモートキャッシュを使用する場合は、SerializationContextInitializer 実装を Data Grid Server に登録する必要があります。

2.2.5. シリアル化コンテキストイニシャライザーの登録

埋め込みキャッシュの場合、Data Grid は java.util.ServiceLoader を使用してアノテーションが付けられた SerializationContextInitializer 実装にシリアル化コンテキストおよびマーシャラーを自動的に登録します。

必要に応じて、SerializationContextInitializer 実装の自動登録を無効にして、手動で登録することができます。

重要

1 つの SerializationContextInitializer 実装を手動で登録する場合は、自動登録が無効になります。その後、他のすべての実装を手動で登録する必要があります。

手順

  1. AutoProtoSchemaBuilder.service アノテーションに false の値を設定します。

    @AutoProtoSchemaBuilder(
          includeClasses = SomeClass.class,
          ...
          service = false
    )
  2. 以下の例のように、プログラムまたは宣言的に SerializationContextInitializer 実装を手動で登録します。

宣言的 (Declarative)

<serialization>
    <context-initializer class="org.infinispan.example.LibraryInitializerImpl"/>
    <context-initializer class="org.infinispan.example.another.SCIImpl"/>
</serialization>

プログラマティック

GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder();
builder.serialization()
       .addContextInitializers(new LibraryInitializerImpl(), new SCIImpl());

2.2.6. Data Grid Server への Protobuf スキーマの登録

Ickle クエリーを実行する場合、または application/x-protostream から application/json などの他のメディアタイプに変換する場合は、Data Grid Server に Protobuf スキーマを登録する必要があります。

前提条件

  • ProtoStream プロセッサーで Protobuf スキーマを生成します。

    生成された Protobuf スキーマは、target/<schemaFilePath>/ ディレクトリーにあります。

  • CREATE 権限を持つユーザーを用意します。

    注記

    セキュリティー認可には、スキーマを追加するための CREATE 権限が必要です。デフォルト設定では、少なくとも deployer のロールが必要です。

手順

次のいずれかの方法で、Protobuf スキーマを Data Grid サーバーに追加します。

  • 任意のブラウザーで Data Grid コンソールを開き、Schema タブを選択してから Add Protobuf schema を選択します。
  • Data Grid コマンドラインインターフェイス (CLI) から --upload= 引数を指定して schema コマンドを使用します。

    [//containers/default]> schema --upload=person.proto person
  • REST API を使用して、POST 要求のペイロードに Protobuf スキーマを含めます。

    POST/rest/v2/schemas/<schema_name>
  • 以下の例のように、Hot Rod クライアントで生成された SerializationContextInitializer 実装を使用して、Protobuf スキーマを登録します。

    /**
     * Register generated Protobuf schema with Data Grid Server.
     * This requires the RemoteCacheManager to be initialized.
     *
     * @param initializer The serialization context initializer for the schema.
     */
    private void registerSchemas(SerializationContextInitializer initializer) {
      // Store schemas in the '___protobuf_metadata' cache to register them.
      // Using ProtobufMetadataManagerConstants might require the query dependency.
      final RemoteCache<String, String> protoMetadataCache = remoteCacheManager.getCache(ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME);
      // Add the generated schema to the cache.
      protoMetadataCache.put(initializer.getProtoFileName(), initializer.getProtoFile());
    
      // Ensure the registered Protobuf schemas do not contain errors.
      // Throw an exception if errors exist.
      String errors = protoMetadataCache.get(ProtobufMetadataManagerConstants.ERRORS_KEY_SUFFIX);
      if (errors != null) {
        throw new IllegalStateException("Some Protobuf schema files contain errors: " + errors + "\nSchema :\n" + initializer.getProtoFileName());
      }
    }
  • SerializationContextInitializer 実装およびカスタムクラスを持つ JAR ファイルを $RHDG_HOME/server/lib ディレクトリーに追加します。

    これを実行すると、Data Grid Server は起動時に Protobuf スキーマを登録します。ただし、スキーマは ___protobuf_metadata キャッシュに保存されたり、クラスター全体で自動的に分散されたりしないため、アーカイブを各サーバーインストールに追加する必要があります。

    注記

    Data Grid Server が application/x-protostream から application/x-java-object への変換を実施する必要がある場合、これを実行する必要があります。この場合、POJO 用の JAR ファイルも追加する必要があります。

次のステップ

以下の例のように、SerializationContextInitializer を Hot Rod クライアントに登録します。

ConfigurationBuilder remoteBuilder = new ConfigurationBuilder();
remoteBuilder.addServer().host(host).port(Integer.parseInt(port));

// Add your generated SerializationContextInitializer implementation.
LibraryInitalizer initializer = new LibraryInitalizerImpl();
remoteBuilder.addContextInitializer(initializer);

2.2.7. 手動によるシリアル化コンテキストイニシャライザーの実装

重要

Data Grid は、SerializationContextInitializer または GeneratedSchema インターフェイスを手動で実装しないことを強く推奨します。

ProtobufTagMarshaller および RawProtobufMarshaller アノテーションを使用して SerializationContextInitializer または GeneratedSchema インターフェイスを手動で実装できます。

ただし、手動の実装には多くの面倒なオーバーヘッドが必要で、エラーが発生する可能性が高くなります。protostream-processor アーティファクトを使用して生成する実装は、ProtoStream マーシャリングを設定するより効率的で信頼できる方法です。

第3章 代替およびカスタムマーシャラー実装の使用

Data Grid は、ProtoStream マーシャラーで Protobuf ベースのマーシャリングを使用することを推奨します。これにより、Ickle クエリーを使用し、Data Grid CLI およびコンソールを使用できます。ただし、必要な場合は、代替マーシャラーまたはカスタムマーシャラー実装を使用できます。

3.1. Java クラスのデシリアライズの許可

セキュリティー上の理由から、Data Grid は任意の Java クラスのデシリアライズを許可しません。JavaSerializationMarshaller または GenericJBossMarshaller を使用する場合は、Java クラスをデシリアライズ許可リストに追加する必要があります。

注記

デシリアライズ許可リストは Cache Manager に適用されるため、Java クラスはすべてのキャッシュによってデシリアライズされます。

手順

  • Data Grid 設定またはシステムプロパティーのデシリアライズ許可リストに Java クラスを追加します。

宣言的 (Declarative)

<infinispan>
  <cache-container>
    <serialization version="1.0"
                   marshaller="org.infinispan.marshall.TestObjectStreamMarshaller">
      <allow-list>
        <class>org.infinispan.test.data.Person</class>
        <regex>org.infinispan.test.data.*</regex>
      </allow-list>
    </serialization>
  </cache-container>
</infinispan>

システムプロパティー

// Specify a comma-separated list of fully qualified class names
-Dinfinispan.deserialization.allowlist.classes=java.time.Instant,com.myclass.Entity

// Specify a regular expression to match classes
-Dinfinispan.deserialization.allowlist.regexps=.*

3.2. JBoss Marshalling の使用

JBoss Marshalling はシリアル化ベースのマーシャリングライブラリーであり、以前の Data Grid バージョンではデフォルトのマーシャラーでしたが、現在は非推奨になりました。

注記

JBoss Marshalling は非推奨になりました。アプリケーションを古いバージョンの Data Grid から移行する際に、一時的な措置としてのみ使用してください。

手順

  1. infinispan-jboss-marshalling 依存関係をクラスパスに追加します。
  2. GenericJBossMarshaller を使用するように Data Grid を設定します。
  3. Java クラスをデシリアライズ許可リストに追加します。

宣言的 (Declarative)

<serialization marshaller="org.infinispan.jboss.marshalling.commons.GenericJBossMarshaller">
  <allow-list>
    <class>org.infinispan.concrete.SomeClass</class>
    <regex>org.infinispan.example.*</regex>
  </allow-list>
</serialization>

プログラマティック

GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder();
builder.serialization()
       .marshaller(new GenericJBossMarshaller())
       .allowList()
       .addRegexps("org.infinispan.example.", "org.infinispan.concrete.SomeClass");

関連情報

3.3. Java シリアライゼーションの使用

Data Grid で Java シリアライゼーションを使用すると、Java Serializable インターフェイスを実装するオブジェクトをマーシャリングできます。

ヒント

Java シリアライゼーションは、ProtoStream マーシャリングよりもパフォーマンスが低下します。Java シリアライゼーションは、そうしなければならない厳密な要件がある場合にのみ使用してください。

手順

  1. JavaSerializationMarshaller を使用するように Data Grid を設定します。
  2. Java クラスをデシリアライズ許可リストに追加します。

宣言的 (Declarative)

<serialization marshaller="org.infinispan.commons.marshall.JavaSerializationMarshaller">
  <allow-list>
    <class>org.infinispan.concrete.SomeClass</class>
    <regex>org.infinispan.example.*</regex>
  </allow-list>
</serialization>

プログラマティック

GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder();
builder.serialization()
       .marshaller(new JavaSerializationMarshaller())
       .allowList()
       .addRegexps("org.infinispan.example.", "org.infinispan.concrete.SomeClass");

3.4. カスタムマーシャラーの使用

Data Grid は、カスタムマーシャラー用に実装できる Marshaller インターフェイスを提供します。

ヒント

カスタムマーシャラーの実装は、起動時に呼び出される initialize() メソッドを使用して設定済みのアクセスリストにアクセスできます。

手順

  1. Marshaller インターフェイスを実装します。
  2. マーシャラーを使用するように Data Grid を設定します。
  3. Java クラスをデシリアライズ許可リストに追加します。

宣言的 (Declarative)

<serialization marshaller="org.infinispan.example.marshall.CustomMarshaller">
  <allow-list>
    <class>org.infinispan.concrete.SomeClass</class>
    <regex>org.infinispan.example.*</regex>
  </allow-list>
</serialization>

プログラマティック

GlobalConfigurationBuilder builder = new GlobalConfigurationBuilder();
builder.serialization()
      .marshaller(new org.infinispan.example.marshall.CustomMarshaller())
      .allowList().addRegexp("org.infinispan.example.*");

第4章 データ変換

Data Grid はトランスコーダーを使用して、メディアタイプで識別されるさまざまなエンコーディング間でデータを変換します。

4.1. Hot Rod DataFormat API

Hot Rod エンドポイントを使用したリモートキャッシュでの読み取り操作および書き込み操作は、デフォルトでクライアントマーシャラーを使用します。Hot Rod は、異なるメディアタイプエンコーディングやマーシャラーを使用してキャッシュ操作を実行するのに使用できる Java クライアントの DataFormat API を提供します。

キーと値の異なるマーシャラー

実行時にキーと値のマーシャラーを上書きできます。

たとえば、Hot Rod クライアントのすべてのシリアル化をバイパスし、リモートキャッシュに保存されている byte[] 配列を読み取るには、以下を実行します。

// Existing RemoteCache instance
RemoteCache<String, Pojo> remoteCache = ...

// IdentityMarshaller is a no-op marshaller
DataFormat rawKeyAndValues =
DataFormat.builder()
          .keyMarshaller(IdentityMarshaller.INSTANCE)
          .valueMarshaller(IdentityMarshaller.INSTANCE)
          .build();

// Creates a new instance of RemoteCache with the supplied DataFormat
RemoteCache<byte[], byte[]> rawResultsCache =
remoteCache.withDataFormat(rawKeyAndValues);
重要

keyMarshaller() および keyType() メソッドでキーに異なるマーシャラーとデータ形式を使用すると、クライアントインテリジェンスルーティングメカニズムに干渉する可能性があり、Data Grid クラスター内の追加のネットワークホップが発生する可能性があります。パフォーマンスが重要な場合は、クライアントとサーバーで鍵に同じエンコーディングを使用する必要があります。

異なるエンコーディングでのデータの読み取り

以下のように、org.infinispan.commons.dataconversion.MediaType で指定された異なるエンコーディングでデータを要求および送信します。

// Existing remote cache using ProtostreamMarshaller
RemoteCache<String, Pojo> protobufCache = ...

// Request values returned as JSON
// Use the UTF8StringMarshaller to convert UTF-8 to String
DataFormat jsonString =
DataFormat.builder()
          .valueType(MediaType.APPLICATION_JSON)
          .valueMarshaller(new UTF8StringMarshaller())
          .build();
RemoteCache<byte[], byte[]> rawResultsCache =
protobufCache.withDataFormat(jsonString);

カスタム値マーシャラーの使用

org.codehaus.jackson.JsonNode オブジェクトとして値を返す以下の例のように、値にカスタムマーシャラーを使用できます。

この例では、Data Grid Server はデータ変換を処理し、指定したメディアタイプに対応していない場合は例外を出力します。

DataFormat jsonNode =
DataFormat.builder()
          .valueType(MediaType.APPLICATION_JSON)
          .valueMarshaller(new CustomJacksonMarshaller()
          .build();

RemoteCache<String, JsonNode> jsonNodeCache =
remoteCache.withDataFormat(jsonNode);

XML で値を返す

以下のコードスニペットは、値を XML として返します。

Object xmlValue = remoteCache
      .withDataFormat(DataFormat.builder()
      .valueType(MediaType.APPLICATION_XML)
      .valueMarshaller(new UTF8StringMarshaller())
      .build())
      .get(key);

たとえば、前述の get(key) 呼び出しは、以下のような値を返します。

<?xml version="1.0" ?><string>Hello!</string>

4.2. 埋め込みキャッシュを使用したオンデマンドのデータ変換

埋め込みキャッシュには、application/x-java-object のデフォルトのリクエストエンコーディングと、キャッシュに設定したメディアタイプに対応するストレージエンコーディングがあります。つまり、Data Grid はアプリケーションからの POJO をキャッシュのストレージエンコーディングにマーシャルし、POJO をアプリケーションに返します。複雑なシナリオによっては、AdvancedCache API を使用して POJO との間のデフォルト変換を他のエンコーディングに変更できます。

以下の例では、withMediaType() メソッドを使用して、オンデマンドで application/json として値を返します。

MediaType を使用した高度なキャッシュ

DefaultCacheManager cacheManager = new DefaultCacheManager();

// Encode keys and values as Protobuf
ConfigurationBuilder cfg = new ConfigurationBuilder();
cfg.encoding().key().mediaType("application/x-protostream");
cfg.encoding().value().mediaType("application/x-protostream");

cacheManager.defineConfiguration("mycache", cfg.build());

Cache<Integer, Person> cache = cacheManager.getCache("mycache");

cache.put(1, new Person("John","Doe"));

// Use Protobuf for keys and JSON for values
Cache<Integer, byte[]> jsonValuesCache = (Cache<Integer, byte[]>) cache.getAdvancedCache().withMediaType("application/x-protostream", "application/json");

byte[] json = jsonValuesCache.get(1);

JSON 形式で返される値

{
   "_type":"org.infinispan.sample.Person",
   "name":"John",
   "surname":"Doe"
}