インメモリデータグリッドを使用するにあたり分散キャッシュを使って自作のデータ型となるエンティティを使うことは避けられないでしょう。今回はHot Rodを使ったDistributed-cacheでどうやって自作のエンティティを使うかを紹介します。
オブジェクトを読み書きするためにはオブジェクトのシリアライズ/デシリアライズが必要になります。 Infinispan を分散キャッシュとして使うにはネットワーク通信が必要ですので、シリアライズ/デシリアライズが必要となります。サーバ内ではオブジェクトはシリアライズされた状態で格納されています。そのため、サーバ側でデシリアライズする必要が無ければクライアント側だけにシリアライズ/デシリアライズの情報やクラスがあれば動きます。シリアライズするにはマーシャラが必要ですが、Infinispan では自作のエンティティに設定をするだけでマーシャラを自動生成してくれます。
全体の流れ
自作のエンティティを使うには以下の手順が必要です。
- 依存関係の追加
- 自作のエンティティを作成
- コンテキストイニシャライザの設定
- ソースコードを自動生成
- Hot Rodクライアントの設定
- 実行
依存関係の追加
Infinispanでシリアライズするにはprotostream-processor
が必要です。依存関係を追加するためpom.xml
に以下の設定を追加します。
pom.xml
<dependency> <groupId>org.infinispan.protostream</groupId> <artifactId>protostream-processor</artifactId> <version>4.3.2.Final</version> </dependency>
自作のエンティティを作成
依存関係を追加することで、APIが使えるようになりました。このAPIを使用して自作のエンティティとそれをシリアライズする設定を記述します。この設定を書くだけでシリアライズ/デシリアライズが自動的に行われます。
自作のエンティティには次の2つの設定が必要です。
- シリアライズするフィールドを指定
- オブジェクトを作るファクトリメソッドを指定
シリアライズするフィールドを指定するには@ProtoField
アノテーションを使用し、シリアライズする順番と必須フィールドかどうかの指定が必ず必要となり、必要に応じてデフォルト値を指定します。フィールドにプリミティブ型を使用する場合はデフォルト値の指定は必須です。
シリアライズする順番はnumber
属性、必須フィールドかどうかはrequired
属性、デフォルト値はdefaultValue
属性を使用します。
オブジェクトを作るファクトリメソッドを指定するには@ProtoFactory
アノテーションでstaticメソッドを指定します。
static メソッドではないメソッドに@ProtoFactory
アノテーションを指定するとビルド時に以下の様なエラーが発生します。
Error:(79,5) java: org.infinispan.protostream.annotations.ProtoSchemaBuilderException: @ProtoFactory annotated method must be static: public chiroito.sample.CustomEntity CustomEntity.create(java.lang.String,int)
自作のエンティティとなるCustomEntity.java
は以下のとおりです。
import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; public class CustomEntity { @ProtoField(number = 1, required = true) String name; @ProtoField(number = 2, required = true, defaultValue = "0") int num; @ProtoFactory public static CustomEntity create(String name, int num) { return new CustomEntity(name, num); } public CustomEntity(String name, int num) { this.name = name; this.num = num; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } @Override public String toString() { return "CustomEntity{" + "name='" + name + '\'' + ", num=" + num + '}'; } }
コンテキストイニシャライザの設定
ソースコードを自動生成するための設定のためだけのインターフェースです。マーシャラを作成して欲しいクラス、スキーマの情報などを指定します。@AutoProtoSchemaBuilder
アノテーションとSerializationContextInitializer
インターフェースの組み合わせで設定します。SerializationContextInitializer
インターフェースのサブインターフェースを作り、@AutoProtoSchemaBuilder
アノテーションのincludeClasses
属性でマーシャラを作成して欲しいクラスを指定するだけと言う理解で良いでしょう。
詳しくはこちらをご覧ください。
protostream/AutoProtoSchemaBuilder.java at master · infinispan/protostream · GitHub
import org.infinispan.protostream.SerializationContextInitializer; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; @AutoProtoSchemaBuilder( includeClasses = { CustomEntity.class }, schemaFileName = "library.proto", schemaFilePath = "proto/", schemaPackageName = "customEntity") interface CustomInitializer extends SerializationContextInitializer { }
ソースコードを自動生成
ここまでそろった後にビルドをすることで、いくつかのソースコードが自動生成されます。ビルドはmvn package
します。自動生成されたコードはtarget/generated-sources/annotations
以下に格納され、コンパイルされてtarget/classes
に.class
ファイルが生成されます。また、この.class
ファイルはjarファイルにも含まれます。作成されない場合はビルド時に他のプロセッサが邪魔をしていないか確認してください。
今回の例では以下の 5 つのファイルが生成されました。
- target/generated-sources/annotations/chiroito/sample/CustomEntity$___Marshaller_6fdc67bee880914a83a620b354601cbfaee4ffec71b6318bc7625017a75101f1.java
- target/generated-sources/annotations/chiroito/sample/CustomInitializerImpl.java
- target/classes/chiroito/sample/CustomEntity$___Marshaller_6fdc67bee880914a83a620b354601cbfaee4ffec71b6318bc7625017a75101f1.class
- target/classes/chiroito/sample/CustomInitializerImpl.class
- target/classes/proto/library.proto
Hot Rodクライアントの設定
最後に設定ファイルに自動生成されたクラスを指定して終わりです。infinispan.client.hotrod.context-initializers
に自動生成されたクラス名を指定します。
infinispan.client.hotrod.server_list=192.168.1.1:11222 infinispan.client.hotrod.context-initializers=chiroito.sample.CustomInitializerImpl
もしくはコードで自動生成されたクラスのインスタンスを与えます。ConfigurationBuilder
クラスのaddContextInitializer
メソッドで指定します。
ConfigurationBuilder cb = new ConfigurationBuilder(); cb.addContextInitializer(new CustomInitializerImpl()); RemoteCacheManager manager = new RemoteCacheManager(cb.build());
実行
それでは実行してみましょう。今回は自作のエンティティをputしてからgetしてみます。 今回はクライアントでのみシリアライズ/デシリアライズするため、サーバにjarファイルを送ったり設定を書いたりは不要です。
public static void main(String[] args) throws Exception{ RemoteCacheManager manager = new RemoteCacheManager(); RemoteCache<String, CustomEntity> c = manager.getCache("mycache"); c.put("key1", new CustomEntity("りんご", 3)); System.out.println(c.get("key1")); manager.close(); }
以下の様にエンティティの中身が出力されれば成功です。
3 23, 2020 9:00:23 午後 org.infinispan.client.hotrod.RemoteCacheManager actualStart INFO: ISPN004021: Infinispan version: Infinispan 'Turia' 10.1.3.Final 3 23, 2020 9:00:23 午後 org.infinispan.client.hotrod.impl.protocol.Codec20 readNewTopologyAndHash INFO: ISPN004006: Server sent new topology view (id=1, age=0) containing 1 addresses: [192.168.1.1:11222] CustomEntity{name='りんご', num=3}