5.2. AutoProtoSchemaBuilder アノテーションへのアプリケーションの移行
以前のバージョンの DataGrid は、ProtoStream API の MessageMarshaller
インターフェイスを使用してマーシャリングを設定します。
MessageMarshaller
API と ProtoSchemaBuilder
アノテーションはどちらも、ProtoStream4.3.4 に対応する DataGrid 8.1.1 で非推奨になりました。
MessageMarshaller
インターフェイスの使用には、以下のいずれかが含まれます。
- Protobuf スキーマを手動で作成します。
-
ProtoSchemaBuilder
アノテーションを Java クラスに追加してから、Protobuf スキーマを生成します。
ただし、ProtoStream マーシャリングを設定するためのこれらの手法は、 Data Grid 8.1.1 以降で使用可能な AutoProtoSchemaBuilder
アノテーションほど効率的で信頼性が高くありません。AutoProtoSchemaBuilder
アノテーションを Java クラスに追加し、Protobuf スキーマと関連するマーシャラーを含む SerializationContextInitializer
実装を生成するだけです。
Red Hat は、AutoProtoSchemaBuilder
アノテーションの使用を開始して、ProtoStream マーシャラーから最良の結果を取得することを推奨します。
次のコード例は、アプリケーションを MessageMarshaller
API から AutoProtoSchemaBuilder
アノテーションに移行する方法を示しています。
5.2.1. 基本的な MessageMarshaller の実装
この例には、デフォルト以外のタイプを使用するいくつかのフィールドが含まれています。コードジェネレーターはデフォルトで int
型を使用するため、text
フィールドの順序は異なり、fixed32
フィールドは生成された Protobuf スキーマタイプと競合します。
SimpleEntry.java
public class SimpleEntry { private String description; private Collection<String> text; private int intDefault; private Integer fixed32; // public Getter, Setter, equals and HashCode methods omitted for brevity }
SimpleEntryMarshaller.java
import org.infinispan.protostream.MessageMarshaller; public class SimpleEntryMarshaller implements MessageMarshaller<SimpleEntry> { @Override public void writeTo(ProtoStreamWriter writer, SimpleEntry testEntry) throws IOException { writer.writeString("description", testEntry.getDescription()); writer.writeInt("intDefault", testEntry.getIntDefault()); writer.writeInt("fix32", testEntry.getFixed32()); writer.writeCollection("text", testEntry.getText(), String.class); } @Override public SimpleEntry readFrom(MessageMarshaller.ProtoStreamReader reader) throws IOException { SimpleEntry x = new SimpleEntry(); x.setDescription(reader.readString("description")); x.setIntDefault(reader.readInt("intDefault")); x.setFixed32(reader.readInt("fix32")); x.setText(reader.readCollection("text", new LinkedList<String>(), String.class)); return x; } }
結果の Protobuf スキーマ
syntax = "proto2"; package example; message SimpleEntry { required string description = 1; optional int32 intDefault = 2; optional fixed32 fix32 = 3; repeated string text = 4; }
AutoProtoSchemaBuilder アノテーションに移行
SimpleEntry.java
import org.infinispan.protostream.annotations.ProtoField; import org.infinispan.protostream.descriptors.Type; public class SimpleEntry { private String description; private Collection<String> text; private int intDefault; private Integer fixed32; @ProtoField(number = 1) public String getDescription() {...} @ProtoField(number = 4, collectionImplementation = LinkedList.class) public Collection<String> getText() {...} @ProtoField(number = 2, defaultValue = "0") public int getIntDefault() {...} @ProtoField(number = 3, type = Type.FIXED32) public Integer getFixed32() {...} // public Getter, Setter, equals and HashCode methods and convenient constructors omitted for brevity }
SimpleEntryInitializer.java
import org.infinispan.protostream.GeneratedSchema; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; @AutoProtoSchemaBuilder(includeClasses = { SimpleEntry.class }, schemaFileName = "simple.proto", schemaFilePath = "proto", schemaPackageName = "example") public interface SimpleEntryInitializer extends GeneratedSchema { }
重要な所見
-
Field 2 は、以前のバージョンの ProtoStream マーシャラーがチェックしなかった
int
として定義されています。 Java
int
フィールドは null 許容ではないため、ProtoStream プロセッサーは失敗します。
Javaint
フィールドは、required
またはdefaultValue
で初期化する必要があります。Java アプリケーションの観点からは、
int
フィールドは 0 で初期化されるため、put 操作で設定されるため、影響を与えることなくdefaultValue
を使用できます。required
への変更は、常に存在する場合、保存されたデータの観点からは問題ではありませんが、さまざまなクライアントで問題が発生する可能性があります。-
互換性を確立するため、Field 3 は、
Type.FIXED32
に明示的に設定する必要があります。 - テキストコレクションは、結果の Protobuf スキーマに対して正しい順序で設定する必要があります。
Protobuf スキーマのテキストコレクションの順序は、移行の前後で同じである必要があります。同様に、移行中に fixed32
タイプを設定する必要があります。
そうでない場合、クライアントアプリケーションは次の例外を出力し、起動に失敗する可能性があります。
Exception ( ISPN004034: Unable to unmarshall bytes )
また、キャッシュされたデータに不完全または不正確な結果が表示される場合もあります。
5.2.2. カスタムタイプを使用した MessageMarshaller の実装
このセクションでは、ProtoStream がネイティブに処理しないフィールドを含む MessageMarshaller
実装の移行例を示します。
次の例では BigInteger
クラスを使用していますが、データグリッドアダプターやカスタムクラスを含め、すべてのクラスに適用されます。
BigInteger
クラスは不変であるため、引数のないコンストラクターはありません。
CustomTypeEntry.java
import java.math.BigInteger; public class CustomTypeEntry { final String description; final BigInteger bigInt; // public Getter, Setter, equals and HashCode methods and convenient constructors omitted for brevity }
CustomTypeEntryMarshaller.java
import org.infinispan.protostream.MessageMarshaller; public class CustomTypeEntryMarshaller implements MessageMarshaller<CustomTypeEntry> { @Override public void writeTo(ProtoStreamWriter writer, CustomTypeEntry testEntry) throws IOException { writer.writeString("description", testEntry.description); writer.writeString("bigInt", testEntry.bigInt.toString()); } @Override public CustomTypeEntry readFrom(MessageMarshaller.ProtoStreamReader reader) throws IOException { final String desc = reader.readString("description"); final BigInteger bInt = new BigInteger(reader.readString("bigInt")); return new CustomTypeEntry(desc, bInt); } }
CustomTypeEntry.proto
syntax = "proto2"; package example; message CustomTypeEntry { required string description = 1; required string bigInt = 2; }
アダプタークラスを使用して移行されたコード
ProtoAdapter
アノテーションを使用して、MessageMarshaller
実装で作成した Protobuf スキーマと互換性のある Protobuf スキーマを生成する方法で CustomType
クラスをマーシャリングできます。
このアプローチでは、次のことができます。
-
CustomTypeEntry
クラスにアノテーションを追加してはなりません。 -
@ProtoAdapter
アノテーションを使用して Protobuf スキーマとマーシャラーの生成方法を制御するCustomTypeEntryAdapter
クラスを作成します。 @AutoProtoSchemaBuilder
アノテーションとともにCustomTypeEntryAdapter
クラスを含めます。注記AutoProtoSchemaBuilder
アノテーションはCustomTypeEntry
クラスを参照しないため、そのクラスに含まれるアノテーションはすべて無視されます。
次の例が示す CustomTypeEntry
クラスの ProtoStream アノテーションを含む CustomTypeEntryAdapter
クラス:
CustomTypeEntryAdapter.java
import java.math.BigInteger; import org.infinispan.protostream.annotations.ProtoAdapter; import org.infinispan.protostream.annotations.ProtoFactory; import org.infinispan.protostream.annotations.ProtoField; @ProtoAdapter(CustomTypeEntry.class) public class CustomTypeEntryAdapter { @ProtoFactory public CustomTypeEntry create(String description, String bigInt) { return new CustomTypeEntry(description, new BigInteger(bigInt)); } @ProtoField(number = 1, required = true) public String getDescription(CustomTypeEntry t) { return t.description; } @ProtoField(number = 2, required = true) public String getBigInt(CustomTypeEntry t) { return t.bigInt.toString(); } }
次の例が示す CustomTypeEntryAdapter
クラスを参照する AutoProtoSchemaBuilder
アノテーションと t もに SerializationContextInitializer
を示しています。
CustomTypeEntryInitializer.java
import org.infinispan.protostream.GeneratedSchema; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; @AutoProtoSchemaBuilder(includeClasses = { CustomTypeEntryAdapter.class }, schemaFileName = "custom.proto", schemaFilePath = "proto", schemaPackageName = "example") public interface CustomTypeAdapterInitializer extends GeneratedSchema { }
アダプタークラスなしで移行されたコード
アダプタークラスを作成する代わりに、ProtoStream アノテーションを CustomTypeEntry
クラスに直接追加できます。
この例では、生成された Protobuf スキーマは、BigInteger
が別個のメッセージであるため、MessageMarshaller
インターフェイスを介して追加されたキャッシュ内のデータと互換性がありません。アダプターフィールドに同じ文字列が書き込まれていても、データのマーシャリングを解除することはできません。
次の例が示す直接 ProtoStream アノテーションを含む CustomTypeEntry
クラス:
CustomTypeEntry.java
import java.math.BigInteger; public class CustomTypeEntry { @ProtoField(number = 1) final String description; @ProtoField(number = 2) final BigInteger bigInt; @ProtoFactory public CustomTypeEntry(String description, BigInteger bigInt) { this.description = description; this.bigInt = bigInt; } // public Getter, Setter, equals and HashCode methods and convenient constructors omitted for brevity }
以下の例は、CustomTypeEntry
および BigIntegerAdapter
クラスを参照する AutoProtoSchemaBuilder
アノテーションと共に SerializationContextInitializer
を示しています。
CustomTypeEntryInitializer.java
import org.infinispan.protostream.GeneratedSchema; import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder; import org.infinispan.protostream.types.java.math.BigIntegerAdapter; @AutoProtoSchemaBuilder(includeClasses = { CustomTypeEntry.class, BigIntegerAdapter.class }, schemaFileName = "customtype.proto", schemaFilePath = "proto", schemaPackageName = "example") public interface CustomTypeInitializer extends GeneratedSchema { }
前述の SerializationContextInitializer
実装から Protobuf スキーマを生成すると、以下の Protobuf スキーマになります。
CustomTypeEntry.proto
syntax = "proto2"; package example; message BigInteger { optional bytes bytes = 1; } message CustomTypeEntry { optional string description = 1; optional BigInteger bigInt = 2; }