Infinispan に Javaオブジェクトを ProtoStream 経由で PUT する完全図解

InfinispanにJavaオブジェクトをPUTしたいけど、「スキーマ」「エンコーディング」「マーシャリング」「protostream」などの専門用語にぶつかってしまい心が折れかけている方を対象に、前提となる基礎知識を丁寧に解説していきます！
- 旧式のJBossMarshallingについては本記事では扱いませんご了承ください。
- Apache camelのcamel-infinispanを使ったサンプルコードについては改めて紹介する予定です！

---
### 🔷 まずはじめに、Infinispanとは？

Infinispan は「インメモリー・データ・グリッド」と呼ばれる分類のデータストアです。 
一言で言えば、**分散キャッシュとNoSQLデータベースの中間に位置する、高速かつスケーラブルなデータストア**です。

Infinispan の特徴：

- **インメモリ処理**：データを主にメモリ上に保持し、高速なアクセスを実現
- **クラスタ対応**：複数ノード構成によりスケーラブルで耐障害性が高い
- **複数APIサポート**：Java API、REST、Hot Rod、Memcached互換
- **永続化対応**：必要に応じてディスクやRDBMSへバックアップ可能
- **分散クエリ**：Ickleクエリによる全文検索、インデックス付き検索が可能

企業システムやマイクロサービスアーキテクチャでのデータ共有・キャッシュ用途として幅広く採用されています。

---

### 🔶 キャッシュとは？

キャッシュとは、**頻繁に使うデータを一時的に保存して、高速にアクセスするための仕組み**です。  
たとえばWebアプリケーションでは、毎回データベースに問い合わせると遅いので、一定期間キャッシュに保存して素早く返すことで、パフォーマンスを改善できます。

キャッシュの種類：

- **ローカルキャッシュ**：単一アプリケーションのメモリ上に保持
- **分散キャッシュ（Infinispanなど）**：複数ノード間でデータ共有しながらキャッシュする

Infinispan では、キャッシュが「ただのMap」ではなく、**トランザクション、永続化、クエリ、バイナリ変換、認可制御などの多機能なコンテナ**になっています。

---

### 🧪 エンコーディングとは？どんな種類がある？

Infinispan における「エンコーディング」とは、**キャッシュに格納するキーと値を、どんな形式でバイナリ化するかを決める処理**です。  
キャッシュ内のデータを「どう保存するか」「どんな形式で外部に見せるか」に直接関わる重要な概念です。

代表的なエンコーディング形式（Media Type）：

| メディアタイプ | 概要 | 用途例 |
|----------------|------|--------|
| `application/x-protostream` | ProtoStream形式のバイナリ（Protobufベース） | Infinispanの推奨形式、高速・省メモリ・他言語対応 |
| `application/json` | JSON形式（テキスト） | 人間が読みやすい、REST API経由の確認などに便利 |
| `application/x-java-object` | Java特有のオブジェクト形式 | Java専用、速度は早いが他言語と連携不可 |
| `text/plain; charset=UTF-8` | 単純な文字列 | テストや確認用途に便利、複雑データには不向き |

---

### 💡 ProtoStream を使うメリット

ProtoStream は Infinispan 向けに最適化された Protobuf（Protocol Buffers）実装で、Java オブジェクトを**軽量バイナリに変換して保存**するために使われます。

主なメリット：

1. **高速かつ省メモリ**：バイナリ形式なので、JSONよりも格納サイズが小さく、処理も高速
2. **多言語対応**：Protobufに基づく形式なので、Go / Python / JavaScript などの他言語でも読み書き可能
3. **REST/HotRod 対応**：エンコード形式を変換して、JSON形式で読み出すことも可能
4. **インデックス付きクエリ対応**：`.proto` スキーマがあることで、検索やフィルタも可能になる

つまり ProtoStream を使えば、**高性能かつ他システムと連携しやすいキャッシュ構造**を実現できます。

---

### ⚠️ エンコーディングしないとどうなる？

Infinispan では、エンコーディングを明示しない場合、デフォルトで `byte[]` として保存されることがあります。

この場合の問題点：

- **構造が不明**：中身のデータ構造が分からず、可視化・クエリが困難
- **他言語から扱えない**：ProtobufやJSONのような共通フォーマットでないため、Java以外でアクセスできない
- **REST APIやCLIで内容確認できない**：表示されるのは単なるバイト列
- **検索・フィルタ不能**：スキーマなしではクエリ機能が使えない

このように、エンコーディングを怠ると、**Infinispan本来の利点を大きく損なう**ことになります。ProtoStreamを使って構造化・高速化・多言語連携を実現しましょう。


# Infinispan に Java オブジェクトを ProtoStream 経由で PUT する完全図解

Java オブジェクトを Infinispan に保存したいとき、「シリアライズして保存」という言葉をよく聞きます。  
でも実際には、「スキーマ定義」や「マーシャラー登録」など、いくつかの準備が必要です。

この記事では、以下のような疑問を解消します：

- `.proto` ファイルって何？どうやって作るの？
- `@Proto` アノテーションって何に使うの？
- `___protobuf_metadata` キャッシュって何者？
- `SerializationContext` って実際どんな役割？

---

## ✅ 処理の全体像（PUT）

![infinispan.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4063686/fc640ba4-3b9f-4c36-ade7-a4e800d35fe2.jpeg)


この図は、Java オブジェクトを Infinispan に保存する際に必要な処理の全体像を示しています。  
ここで重要なのは「何を手動で作り」「何が自動で生成されるのか」を区別して理解することです。

---

## 🔧 手動で作成するもの（自作）

### ① `@Proto` アノテーション付きの POJO クラス

```java
@Proto
public class Person {
  @ProtoField(number = 1)
  String name;

  @ProtoField(number = 2)
  int age;
}
```

これは Java オブジェクトを ProtoStream でシリアライズ可能にするためのクラスです。  
このクラスは後に `.proto` スキーマの元になります。

---

### ② スキーマ定義インターフェース（`@AutoProtoSchemaBuilder`）

```java
@AutoProtoSchemaBuilder(
  includeClasses = { Person.class },
  schemaFileName = "person.proto",
  schemaFilePath = "proto/",
  schemaPackageName = "my.example"
)
public interface PersonSchemaInitializer extends SerializationContextInitializer {}
```

このインターフェースを作成することで、Infinispan のアノテーションプロセッサがビルド時に関連ファイルを自動生成します。

---

## ⚙️ 自動生成されるもの（ビルド時）

| ファイル名 | 内容 |
|------------|------|
| `person.proto` | Protobuf 形式のスキーマファイル |
| `PersonMarshaller` | Java オブジェクト ↔ バイト列の変換器 |
| `PersonSchemaInitializerImpl` | スキーマ／マーシャラーを登録するクラス |

これらは `target/generated-sources/annotations/` などに出力され、アプリ側から利用可能になります。

---

## 💾 クライアント側での登録処理

```java
SerializationContext ctx =
  ProtoStreamMarshaller.getSerializationContext(cacheManager);

SerializationContextInitializer initializer = new PersonSchemaInitializerImpl();
initializer.registerSchema(ctx);
initializer.registerMarshallers(ctx);
```

これにより、クライアント側でも `Person` クラスのバイナリ変換（マーシャリング）が可能になります。

---
## 🚀 実際にデータを put する

```java
Person p = new Person();
p.setName("Taro");
p.setAge(28);

cache.put("taro-key", p); // ProtoStream 形式でシリアライズして保存
```

`value-encoding` が `application/x-protostream` になっているキャッシュに PUT されます。

---

## 🧠 エンコードとマーシャリングの違い

- **エンコード**：どんなフォーマットで保存するかの指定（例：ProtoStream）
- **マーシャリング**：Java ↔ バイト列 の変換そのもの

---

## ✅ 用語整理

| 用語 | 意味 |
|------|------|
| `___protobuf_metadata` | サーバー側で `.proto` を保存する専用キャッシュ |
| `SerializationContext` | クライアント側のスキーマ・変換定義 |
| `Marshaller` | Javaオブジェクトとバイナリを変換するクラス |
| `ProtoStream` | Infinispanが採用している高速バイナリ形式 |

---

## 📚 類似用語の整理：Protobuf / .proto / ProtoStream の違い

Infinispan で ProtoStream を使う際、よく以下のような用語が登場します。  
どれも似たように見えますが、それぞれ役割が異なるため、違いを理解しておくと全体像がスッキリします。

| 用語 | 概要 | Infinispan における役割 |
|------|------|--------------------------|
| **Protocol Buffers（Protobuf）** | Google が開発した、言語非依存のバイナリフォーマットとスキーマ仕様 | データ表現フォーマットの基盤（Infinispan もこれを採用） |
| **`.proto` ファイル** | Protobuf のスキーマ定義ファイル。メッセージの構造を定義する | Infinispan に登録するスキーマファイル。RESTやHotRod経由で利用 |
| **ProtoStream** | Infinispan の Protobuf 実装（Java専用） | Javaオブジェクトとの相互変換（マーシャリング）を担当 |

---

### 🔄 関係性（ざっくり図）

```
JavaのPOJO（@Protoつき）
   ↓ アノテーションプロセッサで変換
person.proto（Protobuf形式のスキーマ定義）
   ↓
Infinispanサーバに登録（___protobuf_metadata）
   ↓
ProtoStreamを使ってJava ↔ バイト列を変換
```

---

### ✅ それぞれの特徴まとめ

#### 🧩 Protocol Buffers（Protobuf）
- Google製のバイナリフォーマット
- JSONより小さく高速
- 多言語対応（Java, Go, Python など）

#### 📄 .proto ファイル
- Protobufの設計書（スキーマ定義）
- `message`, `optional`, `repeated`, `int32`, `string` などで定義
- Infinispanでは `___protobuf_metadata` に登録して使用

#### 🔧 ProtoStream
- Infinispan用の Protobuf 実装
- `@Proto`, `@ProtoField`, `@AutoProtoSchemaBuilder` で自動スキーマ生成
- Javaオブジェクトと Protobufバイト列を変換するための仕組み（Marshaller自動生成）

---


以上です！  
この記事が Infinispan + ProtoStream を理解する足がかりになれば幸いです。