Cache API for Play Framework applications providing both synchronous and asynchronous cache operations
npx @tessl/cli install tessl/maven-com-typesafe-play--play-cache-2-11@2.7.0The Play Framework Cache API provides a comprehensive caching abstraction layer for Play Framework applications. It offers both synchronous and asynchronous APIs for cache operations with support for various cache implementations (EhCache, Caffeine, JCache). The library supports both Scala and Java programming languages with type-safe operations and configurable expiration times.
libraryDependencies += "com.typesafe.play" %% "play-cache" % "2.7.9" to your build.sbt (requires Play Framework 2.7.x)import play.api.cache.{AsyncCacheApi, SyncCacheApi, Cached}
import javax.inject.Inject
import scala.concurrent.duration._import play.cache.AsyncCacheApi;
import play.cache.SyncCacheApi;
import play.cache.Cached;
import play.cache.NamedCache;
import javax.inject.Inject;import play.api.cache.{AsyncCacheApi, SyncCacheApi}
import javax.inject.Inject
import scala.concurrent.Future
import scala.concurrent.duration._
class UserService @Inject()(cache: AsyncCacheApi) {
// Async cache operations
def getUser(id: String): Future[Option[User]] = {
cache.get[User](s"user:$id").flatMap {
case Some(user) => Future.successful(Some(user))
case None =>
// Load from database and cache
loadUserFromDb(id).map { user =>
cache.set(s"user:$id", user, 1.hour)
Some(user)
}
}
}
// Sync cache operations
def getUserSync(id: String): Option[User] = {
cache.sync.get[User](s"user:$id") match {
case Some(user) => Some(user)
case None =>
val user = loadUserFromDbSync(id)
cache.sync.set(s"user:$id", user, 1.hour)
Some(user)
}
}
}import play.cache.AsyncCacheApi;
import play.cache.SyncCacheApi;
import javax.inject.Inject;
import java.util.concurrent.CompletionStage;
import java.util.Optional;
public class UserService {
private final AsyncCacheApi cache;
@Inject
public UserService(AsyncCacheApi cache) {
this.cache = cache;
}
// Async cache operations
public CompletionStage<Optional<User>> getUser(String id) {
return cache.getOptional("user:" + id)
.thenCompose(userOpt -> {
if (userOpt.isPresent()) {
return CompletableFuture.completedFuture(userOpt);
} else {
return loadUserFromDb(id)
.thenCompose(user ->
cache.set("user:" + id, user, 3600)
.thenApply(done -> Optional.of(user))
);
}
});
}
// Sync cache operations
public Optional<User> getUserSync(String id) {
Optional<User> cached = cache.sync().getOptional("user:" + id);
if (cached.isPresent()) {
return cached;
}
User user = loadUserFromDbSync(id);
cache.sync().set("user:" + id, user, 3600);
return Optional.of(user);
}
}The Play Cache API is organized around several key components:
Basic cache operations for storing, retrieving, and removing data with optional expiration times.
// Scala API
trait AsyncCacheApi {
def set(key: String, value: Any, expiration: Duration = Duration.Inf): Future[Done]
def get[T: ClassTag](key: String): Future[Option[T]]
def remove(key: String): Future[Done]
def getOrElseUpdate[A: ClassTag](key: String, expiration: Duration = Duration.Inf)(orElse: => Future[A]): Future[A]
def removeAll(): Future[Done]
lazy val sync: SyncCacheApi
}
trait SyncCacheApi {
def set(key: String, value: Any, expiration: Duration = Duration.Inf): Unit
def get[T: ClassTag](key: String): Option[T]
def remove(key: String): Unit
def getOrElseUpdate[A: ClassTag](key: String, expiration: Duration = Duration.Inf)(orElse: => A): A
}// Java API
public interface AsyncCacheApi {
default SyncCacheApi sync();
<T> CompletionStage<Optional<T>> getOptional(String key);
<T> CompletionStage<T> getOrElseUpdate(String key, Callable<CompletionStage<T>> block, int expiration);
<T> CompletionStage<T> getOrElseUpdate(String key, Callable<CompletionStage<T>> block);
CompletionStage<Done> set(String key, Object value, int expiration);
CompletionStage<Done> set(String key, Object value);
CompletionStage<Done> remove(String key);
CompletionStage<Done> removeAll();
}
public interface SyncCacheApi {
<T> Optional<T> getOptional(String key);
<T> T getOrElseUpdate(String key, Callable<T> block, int expiration);
<T> T getOrElseUpdate(String key, Callable<T> block);
void set(String key, Object value, int expiration);
void set(String key, Object value);
void remove(String key);
}Built-in HTTP response caching for Play Framework actions with ETag and cache header support.
class Cached @Inject() (cache: AsyncCacheApi)(implicit materializer: Materializer) {
def apply(key: RequestHeader => String, caching: PartialFunction[ResponseHeader, Duration]): CachedBuilder
def apply(key: RequestHeader => String): CachedBuilder
def apply(key: String): CachedBuilder
def apply(key: RequestHeader => String, duration: Int): CachedBuilder
def apply(key: RequestHeader => String, duration: Duration): CachedBuilder
}
final class CachedBuilder {
def apply(action: EssentialAction): EssentialAction
def build(action: EssentialAction): EssentialAction
def includeStatus(status: Int): CachedBuilder
def includeStatus(status: Int, duration: Duration): CachedBuilder
def default(duration: Duration): CachedBuilder
def compose(alternative: PartialFunction[ResponseHeader, Duration]): CachedBuilder
}@With(CachedAction.class)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Cached {
String key();
int duration() default 0;
}
public class CachedAction extends Action<Cached> {
public CompletionStage<Result> call(Request req);
}Support for multiple cache instances using named cache injection with Play's DI framework.
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedCache {
String value();
}
public class NamedCacheImpl implements NamedCache, Serializable {
public NamedCacheImpl(String value);
public String value();
}// Type alias in Scala package object
type NamedCache = play.cache.NamedCacheDependency Injection and Named Caches
// Scala types
import akka.Done
import scala.concurrent.Future
import scala.concurrent.duration.Duration
import scala.reflect.ClassTag
import play.api.mvc.{EssentialAction, RequestHeader, ResponseHeader, Result}
// Default implementation classes
class DefaultSyncCacheApi @Inject() (val cacheApi: AsyncCacheApi) extends SyncCacheApi
private[play] final class SerializableResult(constructorResult: Result) extends Externalizable// Java types
import akka.Done;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Callable;
import java.util.Optional;
// Default implementation classes
@Singleton
public class DefaultAsyncCacheApi implements AsyncCacheApi
public class DefaultSyncCacheApi implements SyncCacheApi
public class SyncCacheApiAdapter implements SyncCacheApi