第11章 JSR-107 (JCache) API
11.1. JSR-107 (JCache) API
Red Hat JBoss Data Grid 7.2 より、JCache 1.1.0 API の実装 (JSR-107) が含まれるようになりました。JCache は、一時的な Java オブジェクトをメモリーにキャッシュするために標準の Java API を選択しました。Java オブジェクトをキャッシュすると、読み出しのコストが高いデータ (DB または Web サービス) または計算が困難なデータの使用により発生したボトルネックに対応できます。このようなオブジェクトをメモリーでキャッシュすると、コストの高いラウンドトリップや再計算を行う代わりに、メモリーから直接データを読み出しすることでアプリケーションを高速化することができます。本書では、新しい仕様の Red Hat JBoss Data Grid で JCache を使用する方法と API の主な側面を説明します。
11.2. 依存関係
JCache の依存関係を定義するには、Maven で定義するか、クラスパスに追加します。両方の方法について以下に説明します。
11.2.1. オプション 1: Maven
JCache 実装を使用するには、使用方法に応じて以下の依存関係を Maven の pom.xml に追加する必要があります。
embedded:
<dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-embedded</artifactId> <version>${infinispan.version}</version> </dependency> <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId> <version>1.1.0.redhat-1</version> </dependency>
remote:
<dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-remote</artifactId> <version>${infinispan.version}</version> </dependency> <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId> <version>1.1.0.redhat-1</version> </dependency>
11.2.2. オプション 2: 必要なファイルのクラスパスへの追加
Maven を使用しない場合、必要な jar ファイルが実行時にクラスパスに存在する必要があります。実行時にこれらのファイルが利用できるようにするには、jar ファイルの直接埋め込み、実行時の指定、またはアプリケーションのデプロイに使用されるコンテナーへの追加のいずれかを行います。
埋め込みモード
-
Red Hat カスタマーポータルから
Red Hat JBoss Data Grid 7.2.1 Library
をダウンロードします。 - ダウンロードしたアーカイブをローカルディレクトリーで展開します。
以下のファイルを見つけます。
- jboss-datagrid-7.2.1-library/infinispan-embedded-8.5.0.Final-redhat-9.jar
- jboss-datagrid-7.2.1-library/lib/cache-api-1.1.0.redhat-1.jar
- 実行時に両方の jar ファイルがクラスパス上にあるようにします。
リモートモード
-
Red Hat カスタマーポータルから
Red Hat JBoss Data Grid 7.2.1 Hot Rod Java Client
をダウンロードします。 - ダウンロードしたアーカイブをローカルディレクトリーで展開します。
以下のファイルを見つけます。
- jboss-datagrid-7.2.1-remote-java-client/infinispan-remote-8.5.0.Final-redhat-9.jar
- jboss-datagrid-7.2.1-remote-java-client/lib/cache-api-1.1.0.redhat-1.jar
- 実行時に両方の jar ファイルがクラスパス上にあるようにします。
11.3. ローカルキャッシュの作成
以下の手順にしたがうと、JCache API 仕様の定義どおりにデフォルトの設定オプションを使用して簡単にローカルキャッシュを作成できます。
import javax.cache.*; import javax.cache.configuration.*; // Retrieve the system wide cache manager CacheManager cacheManager = Caching.getCachingProvider().getCacheManager(); // Define a named cache with default JCache configuration Cache<String, String> cache = cacheManager.createCache("namedCache", new MutableConfiguration<String, String>());
デフォルトでは、データは storeByValue
として保存されるよう JCache API によって指定されるため、キャッシュへの操作の外部となるオブジェクト状態の変異はキャッシュに保存されたオブジェクトには影響しません。JBoss Data Grid では、シリアライズやマーシャリングを使用してキャッシュに保存するコピーを作成し、これにより仕様に準拠します。そのため、デフォルトの JCache 設定を Infinispan で使用する場合、保存するデータはマーシャル可能である必要があります。
この代わりに、参照でデータを格納するよう JCache を設定することもできます。これには、以下を呼び出します。
Cache<String, String> cache = cacheManager.createCache("namedCache", new MutableConfiguration<String, String>().setStoreByValue(false));
11.3.1. ライブラリーモード
ライブラリーモードでは、CachingProvider.getCacheManager
の URL
パラメーターで設定ファイルの場所を指定して CacheManager
を設定することができます。これにより、設定ファイルでクラスター化されたキャッシュを定義する機会が与えられ、キャッシュの名前を CacheManager.getCache
メソッドに渡して事前設定されたメソッドへの参照を取得できます。これ以外の場合は、CacheManager.createCache
から作成されたローカルキャッシュのみを使用できます。
11.3.2. クライアントサーバーモード
リモート CacheManager
のクライアントサーバーモードに固有する設定は、CachingProvider.getCacheManager
の properties
パラメーターを使用して標準の Hot Rod クライアントプロパティーを渡して実行されます。参照されるリモートサーバーは稼働中である必要があり、リクエストを受信できなければなりません。
指定がない場合は、デフォルトのアドレスとポートが使用されます (127.0.0.1:11222)。さらに、ライブラリーモードとは異なり、キャッシュの参照が初めて取得されたときにキャッシュを内部で登録するために CacheManager.createCache
を使用する必要があります。後続のクエリーは、CacheManager.getCache
を使用して実行できます。
11.4. データの格納および読み出し
JCache API は java.util.Map または java.util.concurrent.ConcurrentMap を拡張しませんが、キーバリュー API を提供し、データを保存および読み出します。
import javax.cache.*; import javax.cache.configuration.*; CacheManager cacheManager = Caching.getCachingProvider().getCacheManager(); Cache<String, String> cache = cacheManager.createCache("namedCache", new MutableConfiguration<String, String>()); cache.put("hello", "world"); // Notice that javax.cache.Cache.put(K) returns void! String value = cache.get("hello"); // Returns "world"
標準の java.util.Map とは異なり、javax.cache.Cache には put
および getAndPut
と呼ばれる 2 つの基本的な put メソッドが含まれます。put は void
を返し、getAndPut はキーに関連する以前の値を返します。JCache では javax.cache.Cache.getAndPut(K) が java.util.Map.put(K) と同等になります。
JCache API はスタンドアロンのキャッシングのみに対応しますが、永続ストアでプラグ可能で、クラスターや分散を考慮して設計されています。javax.cache.Cache が 2 つの put メソッドを提供する理由は、標準の java.util.Map put コールはインプリメンターに以前の値の計算を強制するためです。永続ストアが使用された場合またはキャッシュが分散された場合、以前の値を戻す操作はコストが高くなる可能性があり、ユーザーは多くの場合で戻り値を使用せずに標準の java.util.Map.put(K) を呼び出します。そのため、JCache ユーザーは戻り値が関係するかどうかを考慮する必要があります。以前の値を返す場合は、 javax.cache.Cache.getAndPut(K) を呼び出す必要があります。その他の場合は java.util.Map.put(K) を呼び出して、コストが高くなる可能性がある以前の値を戻す操作を行わないようにします。
11.5. java.util.concurrent.ConcurrentMap API と javax.cache.Cache API の比較
以下は、java.util.concurrent.ConcurrentMap API および javax.cache.Cache API によって提供されるデータ操作 API の簡単な比較になります。
操作 | java.util.concurrent.ConcurrentMap<K,V> | javax.cache.Cache<K,V> |
---|---|---|
格納し、戻り値はなし。 |
該当なし |
void put(K key) |
格納し、以前の値を返す。 |
V put(K key) |
V getAndPut(K key) |
存在しない場合は格納。 |
V putIfAbsent(K key, V Value) |
boolean putIfAbsent(K key, V value) |
読み出し |
V get(Object key) |
V get(K key) |
存在する場合は削除。 |
V remove(Object key) |
boolean remove(K key) |
削除し、以前の値を返す。 |
V remove(Object key) |
V getAndRemove(K key) |
条件を削除。 |
boolean remove(Object key, Object value) |
boolean remove(K key, V oldValue) |
存在する場合は置き換え。 |
V replace(K key, V value) |
boolean replace(K key, V value) |
置き換え、以前の値を返す。 |
V replace(K key, V value) |
V getAndReplace(K key, V value) |
条件を置き換え。 |
boolean replace(K key, V oldValue, V newValue) |
boolean replace(K key, V oldValue, V newValue) |
2 つの API を比較すると、JCache はコストの高いネットワークの操作や IO 操作を避けるために以前の値を返さないようにします。これは、JCache API の設計で最も重要な原則です。実際に、 分散キャッシュでは計算のコストが高いため、java.util.concurrent.ConcurrentMap に存在し、javax.cache.Cache には存在しない操作があります。唯一の例外が、キャッシュのコンテンツでの繰り返しです。
操作 | java.util.concurrent.ConcurrentMap<K,V> | javax.cache.Cache<K,V> |
---|---|---|
キャッシュサイズの計算。 |
int size() |
該当なし |
キャッシュのすべてのキーを返す。 |
Set<K> keySet() |
該当なし |
キャッシュのすべての値を返す。 |
Collection<V> values() |
該当なし |
キャッシュのすべてのエントリーを返す。 |
Set<Map.Entry<K, V>> entrySet() |
該当なし |
キャッシュでの繰り返し。 |
keySet、values、または entrySet での use |
Iterator<Cache.Entry<K, V>> iterator() |
11.6. JCache インスタンスのクラスター化
標準の API を使用したクラスターキャッシュの可能性を提供するために、Red Hat JBoss Data Grid 実装は仕様を超えた機能を提供します。キャッシュをレプリケートするために次のような設定ファイルがあるとします。
<namedCache name="namedCache"> <clustering mode="replication"/> </namedCache>
次のコードを使用するとキャッシュのクラスターを作成することができます。
import javax.cache.*; import java.net.URI; // For multiple cache managers to be constructed with the standard JCache API // and live in the same JVM, either their names, or their classloaders, must // be different. // This example shows how to force their classloaders to be different. // An alternative method would have been to duplicate the XML file and give // it a different name, but this results in unnecessary file duplication. ClassLoader tccl = Thread.currentThread().getContextClassLoader(); CacheManager cacheManager1 = Caching.getCachingProvider().getCacheManager( URI.create("infinispan-jcache-cluster.xml"), new TestClassLoader(tccl)); CacheManager cacheManager2 = Caching.getCachingProvider().getCacheManager( URI.create("infinispan-jcache-cluster.xml"), new TestClassLoader(tccl)); Cache<String, String> cache1 = cacheManager1.getCache("namedCache"); Cache<String, String> cache2 = cacheManager2.getCache("namedCache"); cache1.put("hello", "world"); String value = cache2.get("hello"); // Returns "world" if clustering is working // -- public static class TestClassLoader extends ClassLoader { public TestClassLoader(ClassLoader parent) { super(parent); } }
11.7. 複数のキャッシュプロバイダー
キャッシュプロバイダーはオーバーロードされた getCachingProvider()
メソッドを使用して javax.cache.Caching
から取得されます。デフォルトでは、このメソッドはクラスパスで見つかった META-INF/services/javax.cache.spi.CachingProvider
ファイルをロードしようとします。1 つのファイルが見つかった場合、そのファイルが使用中のキャッシュプロバイダーを判断します。
複数のキャッシュプロバイダーがある場合、以下のメソッドの 1 つを使用して特定のプロバイダーを選択します。
-
getCachingProvider(ClassLoader classLoader)
-
getCachingProvider(String fullyQualifiedClassName)
別のキャッシュプロバイダーに変更する場合、デフォルトのクラスパスにそのプロバイダーが存在することを確認してください。または、上記のメソッドの 1 つを使用してそのプロバイダーを選択してください。
検出されたまたはCaching
クラスによってロードされた javax.cache.spi.CachingProviders
は内部レジストリーで維持されます。同じキャッシュプロバイダーの後続リクエストは、キャッシュプロバイダー実装をリロードまたは再インスタンス化する代わりに、このレジストリーから返されます。現在のキャッシュプロバイダーを表示するには、以下のいずれかのメソッドを使用します。
-
getCachingProviders()
- デフォルトクラスローダーのキャッシュプロバイダーのリストを提供します。 -
getCachingProviders(ClassLoader classLoader)
- 指定されたクラスローダーのキャッシュプロバイダーのリストを提供します。