Let’s Play! - My Play Framework Learning Journey (7) - Play Caching with Caffeine

Updated on 2017-05-16: Added enabling of CacheAnnotationsModule in Play configuration file.

Play provides a simple Cache API called(surprisingly) CacheApi, and a Cached type for HTTP Response caching. CacheApi is only supports EhCache as cache implementation.

I have used Caffeine with Spring Caching in my previous project, so I would like to do similar annotation based caching in Play. The good thing is Caffeine has JCache(JSR-107) Provider extensions, and Play makes it easy to inject other caching dependencies using Guice.

To use Caffeine:

  • Add Caffeine dependency:
libraryDependencies ++= Seq(
  "com.github.ben-manes.caffeine" % "caffeine" % "2.4.0",
  "com.github.ben-manes.caffeine" % "jcache" % "2.4.0",
  "org.jsr107.ri" % "cache-annotations-ri-guice" % "1.0.0",
  "javax.enterprise" % "cdi-api" % "2.0-PFD2"
)

The cdi dependency is needed due to an issue in the dependency of javax.cache:cache-api:1.0. Detailed discussions are here

  • Define the CacheManager bindings in Guice module. JCache has a mechanism to discover the implementation, so if there is only one library implementing JCache in your dependencies, you just need to do this:
bind[CacheManager]toInstance(Caching.getCachingProvider.getCacheManager)

Otherwise, you can do this:

val provider = Caching.getCachingProvider(classOf[CaffeineCachingProvider].getName)
val cacheManager = provider.getCacheManager(provider.getDefaultURI, provider.getDefaultClassLoader)
    bind[CacheResolverFactory].toInstance(new DefaultCacheResolverFactory(cacheManager))
    bind[CacheManager].toInstance(cacheManager)
  • Add the following line in application.conf:
play.modules.enabled += "org.jsr107.ri.annotations.guice.module.CacheAnnotationsModule"
  • Use the JCache annotations such as @CacheResult at places where results should be cached. An example can be found in this post. It is reproduced here.
@Singleton
class SomeRepo @Inject() (val dbConfig: DatabaseConfig[JdbcProfile], val tables: Tables, cacheManager: CacheManager) {

  import dbConfig.profile.api._

  val db = dbConfig.db

  @CacheResult(cacheName="default")
  def findAll: Future[Seq[SomeTable]] = db.run(tables.someTable.result)

}
  • As mentioned in Caffeine’s JCache documentation, the Caffeine JCache provider is configured using Typesafe Config library. This means that Caffeine JCache provider can be configured in the same way that Play is configured. What I did is to put Caffeine configurations in a file called caffeine.conf, and included it in the main application.conf.

Caffeine provides a reference.conf for the configuration options.

  • For testing, you can just use a mock CacheManager without mocking any of its behavior.
val mockCacheManager = mock[CacheManager]
override def configure() = {
  ...
  bind[CacheManager]toInstance(mockCacheManager)
}

Additional References

Go Top
comments powered by Disqus