JSR 107: JCACHE - Java Temporary Caching API

Javaアプリケーション開発時に、繰り返し利用されるオブジェクトをキャッシュすることでパフォーマンスを劇的に向上させられる場合がある。「JSR 107: JCACHE - Java Temporary Caching API」は、そのような目的のための標準的なキャッシュ機構を提供するAPIだ。

JCacheは2005年にAPIのドラフトが公開されているが、それ以降目立った活動はなかった。しかし今年になって高性能なキャッシュライブラリであるEhcacheの開発者の一人、Greg Luck氏がスペックリードに加わり、再び標準化作業が進められるのではないかと期待されている。EhcacheはThe Apache Software License, Version 2.0のもとに公開されているオープンソースのキャッシュライブラリで、もともとJCacheとは独立したプロダクトだが、バージョン1.3よりJCache APIのドラフト仕様をサポートするようになった。

JCache APIのドラフトをサポートしたEhcacheを試す

JCache APIのドラフトはjava.net上のプロジェクトサイトにて公開されている。ただしこの内容には不完全な部分が多く、最終リリースまでに大きく変更される可能性が高いとGreg Luck氏は語っている。EhcaheのJCacheサポート機能はこの不完全なドラフトに基づいて実装されているため、現時点ではEhcacheで提供される機能と組み合わせて利用するのが現実的と言える。

Ehcacheはこのページより入手できる。JCache APIは1.3以降でサポートされており、本稿執筆時点での最新版はバージョン1.4.0 Beta。ダウンロードしたファイルを展開して、ehcache-1.4.0-beta.jar(1.4.0 Betaの場合)とlibディレクトリに含まれる各JARファイルをクラスパスに含めて利用すればよい。

Ehcacheでは、JCache APIの実装をnet.sf.jsr107cacheパッケージとして提供している(これは/lib/jsr107cache-1.0.jarに含まれる)。また、各インタフェースの実装クラスはnet.sf.ehcache.jcacheパッケージ以下に含まれている。Ehcacheが独自に提供するクラス群と名前が衝突しているので、使用の際には注意が必要。

まず、JCache APIで提供される機能のみでキャッシュ機構を利用してみる。キャッシュを作成する基本的な手順は以下のようになる。

  1. CacheManagerを取得する
  2. CacheManagerからCacheFactoryを取得する
  3. CacheFactoryからCacheオブジェクトを生成する…Cacheの設定はjava.util.Mapに格納する

CacheをCacheManagerに登録する

CacheインタフェースはMapインタフェースを継承しているため、キャッシュしたいオブジェクトはput()メソッドによってキーとセットで格納すればよい。逆にキャッシュからオブジェクトを取り出す場合には、getEntry()メソッドを用いてCacheEntry(これはMap.Entryの継承クラス)オブジェクトを取得する。なお、CacheManagerはgetInstance()メソッドを用いることで環境に唯一のインスタンスを取得できる。

実際にJCache APIを利用したプログラム例をリスト1に示す。ここではインターネットからロードしたImageIconオブジェクトをキャッシュに保存し、それを再度取得する例を示している。Cacheに対する設定内容についてはJCache APIのドラフトには詳細が記されていないため、CacheFactoryの実装クラスであるnet.sf.ehcache.jcache.JCacheFactoryクラスのドキュメントを参照のこと。

リスト1 CacheSample.java - JCache APIの使用例

package apisample.jcache;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.swing.ImageIcon;
import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheEntry;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;

public class CacheSample {
    public CacheSample() {
        // CacheManagerを取得
        CacheManager manager = CacheManager.getInstance();

        try {
            // CacheFactoryを取得
            CacheFactory cacheFactory = manager.getCacheFactory();

            // Cacheの設定をMapに書き込み、CacheFactoryからCacheを生成
            Map config = new HashMap();
            config.put("name", "sampleCache"); 
            config.put("maxElementsInMemory", "10");
            config.put("memoryStoreEvictionPolicy", "LFU"); // LFU,LRU,FIFOのどれか
            config.put("overflowToDisk", "true");
            config.put("eternal", "false");
            config.put("timeToLiveSeconds", "5");
            config.put("timeToIdleSeconds", "5");
            config.put("diskPersistent", "false");
            config.put("diskExpiryThreadIntervalSeconds", "120");

            Cache cache = cacheFactory.createCache(config);

            // CacheManagerにCacheを登録
            manager.registerCache("sampleCache", cache);
        } catch (CacheException ex) {
            ex.printStackTrace();
        }

        Cache cache = manager.getCache("sampleCache");
        try {
            // Cacheにオブジェクトを追加
            ImageIcon icon = new ImageIcon
                    (new URL("http://journal.mycom.co.jp/images/ci_mycomjournal.gif"));
            cache.put("image1", icon);
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }        
    }

    public static void main(String[] args) {
        CacheSample sample = new CacheSample();

        // CacheManagerからCacheを取得し、オブジェクトを取り出す
        CacheManager singletonManager = CacheManager.getInstance();
        Cache cache = singletonManager.getCache("sampleCache");
        CacheEntry entry = cache.getCacheEntry("image1");
        ImageIcon value = (ImageIcon)entry.getValue();
        System.out.println(value);
    }
}

続いて、Ehcacheの提供するCacheManager(JCache APIのものと区別するため、ここではパッケージ名も含めてnet.sf.ehcache.CacheManagerと記述する)を利用して、JCache APIのCacheを管理する例を紹介しよう。EhcaheではCacheインタフェースの実装クラスとしてnet.sf.ehcache.jcache.JCacheというクラスが用意されている。JCacheオブジェクトはEhcacheで通常利用するnet.sf.ehcache.Cacheオブジェクト(JCache APIのものではないので注意)を元に生成することができる。

net.sf.ehcache.Cacheオブジェクトを生成する方法はいくつかあるが、もっとも簡単なのはnet.sf.ehcache.CacheManagerのaddCache()メソッドによってデフォルト設定のキャッシュを作ってしまうことだ。なお、net.sf.ehcache.CacheManagerはcreate()メソッドによって環境に唯一のマネージャを生成できる。すでに生成済みのマネージャを取得する場合はgetInstance()メソッドを使用する。

実際のプログラム例をリスト2に示す。現時点ではJCache APIのドラフトで定義されているキャッシュ機構よりも、Ehcacheに実装されているものの方が完成度が高いため、こちらの例の方がより現実的と言えるだろう。

リスト2 CacheSampleWithEhcache.java

package apisample.jcache;

import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.jcache.JCache;
import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheEntry;

public class CacheSampleWithEhcache {
    public CacheSampleWithEhcache() {
        // EhcacheのCacheManagerを作成してEhcaheを追加
        CacheManager manager = CacheManager.create();
        manager.addCache("sampleCache");

        // EhcacheからJCacheを作成
        Cache cache = new JCache(manager.getCache("sampleCache"), null);
        try {
            // JCacheにオブジェクトを追加
            ImageIcon icon = new ImageIcon
                    (new URL("http://journal.mycom.co.jp/images/ci_mycomjournal.gif"));
            cache.put("image1", icon);
        } catch (MalformedURLException ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        CacheSampleWithEhcache sample = new CacheSampleWithEhcache();

        // CacheManagerからJCacheを取得し、オブジェクトを取り出す
        CacheManager singletonManager = CacheManager.getInstance();
        Cache cache = singletonManager.getJCache("sampleCache");
        CacheEntry entry = cache.getCacheEntry("image1");
        ImageIcon value = (ImageIcon)entry.getValue();
        System.out.println(value);
    }
}

Greg Luck氏によれば、今後はEhcacheによる成果も取り入れながらJCache APIの詳細を再検討していくとのことである。なお、Ehcache自身は単純なキャッシュ機構の実装だけでなく、さまざまなケースに応じた機能を提供している。また、Ehcacheを元にさまざまなキャッシュパターンを実装した「Ehcache Constructs」というプロダクトもある。JCache APIの登場が待てない開発者は、Ehcacheの利用を検討してみるのもいいだろう。