JCache（Ehcache 3）のメソッドの結果キャッシュを軽く試してみた feat. Guice

使ったもの

- JCache API 1.0.0
- Ehcache 3
- Guice

```groovy
// Groovy Version: 2.4.5 JVM: 1.8.0_77 Vendor: Oracle Corporation OS: Mac OS X
@Grab('javax.cache:cache-api:1.0.0')
@Grab('org.ehcache:ehcache:3.2.0')
@Grab('org.jsr107.ri:cache-annotations-ri-guice:1.0.0')
@Grab('com.google.inject:guice:4.1.0')

import com.google.inject.AbstractModule
import com.google.inject.Guice
import com.google.inject.Injector
import org.jsr107.ri.annotations.guice.module.CacheAnnotationsModule

import javax.cache.CacheManager
import javax.cache.Caching
import javax.cache.annotation.CacheDefaults
import javax.cache.annotation.CacheResult
import javax.cache.configuration.Configuration
import javax.cache.configuration.MutableConfiguration
import javax.cache.expiry.AccessedExpiryPolicy
import javax.cache.expiry.Duration
import javax.cache.spi.CachingProvider
import java.text.SimpleDateFormat
import java.util.concurrent.TimeUnit

@CacheDefaults(cacheName = 'simpleCache')
class Sample {
    @CacheResult
    String hello() {
        new SimpleDateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(new Date())
    }
}

CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();

Configuration<Object, Object> config =
        new MutableConfiguration<Object, Object>()
                .setTypes(Object.class, Object.class)
                // キャッシュの有効期間は1秒
                .setExpiryPolicyFactory(AccessedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, 1)));

cacheManager.createCache('simpleCache', config)

Injector injector = Guice.createInjector(new CacheAnnotationsModule())

Sample sample = injector.getInstance(Sample.class);
println sample.hello()
println sample.hello()
println sample.hello()
println sample.hello()
println sample.hello()
// ↑は全て同じ時刻が出力されること
TimeUnit.SECONDS.sleep(1)
// キャッシュ切れで取得し直して上とは違う時刻が出力されること
println sample.hello()
```

結果

```bash
2016-12-24 14:04:27.284
2016-12-24 14:04:27.284
2016-12-24 14:04:27.284
2016-12-24 14:04:27.284
2016-12-24 14:04:27.284
2016-12-24 14:04:28.312 # 結果がここだけ上と違う。想定通り
```

CDIとかを使わないでメソッドの結果をキャッシュするのをサクッと試そうとしただけなのに、20時間ほど要してしまった。。

最初の想定としては、

1. JCache APIと実装を依存性に追加
1. POJOのキャッシュしたいメソッドに `@javax.cache.annotation.CacheResult` 付ける

くらいで出来るだろうと思っていたが、全然違った。

実際は、

1. JCache APIと実装を依存性に追加
1. [CDIやSpring Framework以外で使うためにはGuiceしか選択肢がなさそう](http://search.maven.org/#search%7Cga%7C1%7Corg.jsr107.ri)なのでGuiceを依存性に追加
1. Guice用の`cache-annotations-ri`を依存性に追加
1. POJOのキャッシュしたいメソッドに `@javax.cache.annotation.CacheResult` 付ける
1. `javax.cache.annotation.CacheResolverFactory`やJCacheのInterceptorの設定をする

というように、かなり込み入ったことが必要だった。

### よもやま

keyとvalueを共に`java.lang.Object`で利用しているが、これは`org.jsr107.ri.annotations.DefaultCacheResolverFactory#getCacheResolver`が`javax.cache.CacheManager#getCache(java.lang.String)`を中で呼んでいて[Ehcacheの実装を見たところ](https://github.com/ehcache/ehcache3/blob/v3.2.0/107/src/main/java/org/ehcache/jsr107/Eh107CacheManager.java#L288-L292)、key, valueが`Object`になっていないとダメなことが分かったため。

`javax.cache.CacheManager#getCache(java.lang.String, java.lang.Class<K>, java.lang.Class<V>)`を呼ぶようにすれば、任意の型で扱えるはずなので、`CacheResolverFactory`を自分で実装すれば変えられそう。とりあえず今回はさっさと動かすことを重視してkey, valueを`Object`にした。

Interceptorの設定は完全に[Fathom](http://fathom.gitblit.com)というフレームワークのコードを利用している。ありがたや。。今度このフレームワーク自体も試してみたい。

[追記] ↑ @kazuhira_r さんのご指摘により、`org.jsr107.ri.annotations.guice.module.CacheAnnotationsModule`を使うようにしてコード量削減。

### 所感

JCacheはBean Validationのように雰囲気とノリと勢いで試せるものではなかった。ドキュメントのサンプルもイマイチ役に立たなかったし。

### 参考
- https://docs.google.com/document/d/1MZQstO9GJo_MUMy5iD5sxCrstunnQ1f85ekCng8LcqM/edit?usp=sharing
- http://www.ehcache.org
- http://d.hatena.ne.jp/Kazuhira/20131204/1386163253
- http://d.hatena.ne.jp/Kazuhira/20150709/1436452506
- https://github.com/gitblit/fathom/blob/master/fathom-jcache/src/main/java/fathom/jcache/JCacheModule.java