Play 2.4.6 - Custom play cache implementation not working on Java

1,238 views
Skip to first unread message

Thibault Meyer

unread,
Jan 31, 2016, 7:54:38 AM1/31/16
to play-framework
Hi,

I try to implement a custom Cache class using Redis rather than EH Cache (cause application will be put on multiple app servers with load balancing). 

Is plan in the release 2.5 to have a better cache implementation ? or with ability to use it on real production env (multiple app servers) ?


Java implementation of Play Cache seem not working. I got the following error :


com.google.inject.ConfigurationException: Guice configuration errors:


1) No implementation for play.cache.CacheApi was bound.
 
while locating play.cache.CacheApi


1 error
 at com
.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042) ~[guice.jar:na]
 at com
.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001) ~[guice.jar:na]
 at com
.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051) ~[guice.jar:na]
 at play
.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:321) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.inject.DelegateInjector.instanceOf(DelegateInjector.java:22) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.cache.Cache.cacheApi(Cache.java:14) ~[play-cache_2.11-2.4.6.jar:2.4.6]
 at play
.cache.Cache.getOrElse(Cache.java:36) ~[play-cache_2.11-2.4.6.jar:2.4.6]
 at models
.AccountModel.getWalletBalanceAsCents(AccountModel.java:968) ~[classes/:2.4.6]
 at models
.AccountModel.getWalletBalance(AccountModel.java:1000) ~[classes/:2.4.6]
 at views
.html.clientview.SideBarClientView_Scope0$SideBarClientView.balance$1(SideBarClientView.template.scala:32) ~[classes/:na]
 at views
.html.clientview.SideBarClientView_Scope0$SideBarClientView.apply(SideBarClientView.template.scala:43) ~[classes/:na]
 at views
.html.clientview.HomeClientView_Scope0$HomeClientView.apply(HomeClientView.template.scala:41) ~[classes/:na]
 at views
.html.clientview.HomeClientView_Scope0$HomeClientView.render(HomeClientView.template.scala:80) ~[classes/:na]
 at views
.html.clientview.HomeClientView.render(HomeClientView.template.scala) ~[classes/:na]
 at controllers
.client.HomeClientController.GET_Home(HomeClientController.java:35) ~[classes/:na]
 at router
.Routes$$anonfun$routes$1$$anonfun$applyOrElse$31$$anonfun$apply$31.apply(Routes.scala:1781) ~[classes/:na]
 at router
.Routes$$anonfun$routes$1$$anonfun$applyOrElse$31$$anonfun$apply$31.apply(Routes.scala:1781) ~[classes/:na]
 at play
.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:136) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$14$$anon$3$$anon$1.invocation(HandlerInvoker.scala:127) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.core.j.JavaAction$$anon$1.call(JavaAction.scala:70) ~[play_2.11-2.4.6.jar:2.4.6]
 at
Global$2.call(Global.java:84) ~[classes/:na]
 at play
.mvc.Security$AuthenticatedAction.call(Security.java:56) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.6.jar:2.4.6]
 at scala
.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) ~[scala-library-2.11.7.jar:na]
 at scala
.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) ~[scala-library-2.11.7.jar:na]
 at play
.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:40) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:70) ~[play-iteratees_2.11-2.4.6.jar:2.4.6]
 at play
.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:32) ~[play_2.11-2.4.6.jar:2.4.6]
 at scala
.concurrent.impl.Future$.apply(Future.scala:31) ~[scala-library-2.11.7.jar:na]
 at scala
.concurrent.Future$.apply(Future.scala:492) ~[scala-library-2.11.7.jar:na]
 at play
.core.j.JavaAction.apply(JavaAction.scala:94) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:104) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:103) ~[play_2.11-2.4.6.jar:2.4.6]
 at scala
.Option.map(Option.scala:146) ~[scala-library-2.11.7.jar:na]
 at play
.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:103) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:96) ~[play_2.11-2.4.6.jar:2.4.6]
 at play
.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524) ~[play-iteratees_2.11-2.4.6.jar:2.4.6]
 at play
.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524) ~[play-iteratees_2.11-2.4.6.jar:2.4.6]
 at play
.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560) ~[play-iteratees_2.11-2.4.6.jar:2.4.6]
 at play
.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560) ~[play-iteratees_2.11-2.4.6.jar:2.4.6]
 at play
.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$13.apply(Iteratee.scala:536) ~[play-iteratees_2.11-2.4.6.jar:2.4.6]
 at play
.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$13.apply(Iteratee.scala:536) ~[play-iteratees_2.11-2.4.6.jar:2.4.6]
 at scala
.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) ~[scala-library-2.11.7.jar:na]
 at scala
.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) ~[scala-library-2.11.7.jar:na]
 at akka
.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40) [akka-actor_2.11.jar:na]
 at akka
.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397) [akka-actor_2.11.jar:na]
 at scala
.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [scala-library-2.11.7.jar:na]
 at scala
.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [scala-library-2.11.7.jar:na]
 at scala
.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.7.jar:na]
 at scala
.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.7.jar:na]




application.conf

play.modules.disabled += "play.api.cache.EhCacheModule"
play.modules.enabled += "modules.RedisCacheModule"



RedisCacheModule.java

package modules;

import play.Logger;
import play.api.Configuration;
import play.api.Environment;
import play.api.inject.Binding;
import play.api.inject.Module;
import play.cache.CacheApi;
import play.cache.NamedCacheImpl;
import scala.collection.Seq;

import javax.inject.Singleton;

public class RedisCacheModule extends Module {

@Override
public Seq<Binding<?>> bindings(final Environment environment, final Configuration configuration) {
final String defaultCacheName = configuration.underlying().getString("play.cache.defaultCache");
Logger.info("defaultCache: {}", defaultCacheName);
return seq(
bind(CacheApi.class).qualifiedWith(new NamedCacheImpl(defaultCacheName)).to(RedisCacheApi.class).in(Singleton.class)
);
}
}


RedisCacheApi.java

package modules;

import com.google.inject.Singleton;
import play.Logger;
import play.cache.CacheApi;

import java.util.concurrent.Callable;

@Singleton
public class RedisCacheApi implements CacheApi {

public RedisCacheApi() {
Logger.info("RedisCacheApi::constructor");
}

@Override
public <T> T get(String key) {
Logger.info("RedisCacheApi::get");
return null;
}

@Override
public <T> T getOrElse(String key, Callable<T> block, int expiration) {
Logger.info("RedisCacheApi::getOrElse");
return null;
}

@Override
public <T> T getOrElse(String key, Callable<T> block) {
Logger.info("RedisCacheApi::getOrElse");
return null;
}

@Override
public void set(String key, Object value, int expiration) {
Logger.info("RedisCacheApi::set");
}

@Override
public void set(String key, Object value) {
Logger.info("RedisCacheApi::set");
}

@Override
public void remove(String key) {
Logger.info("RedisCacheApi::remove");
}
}



Do you have any idea why it's not working ? In the worst case, i have to obtain a NullPointerException because all overrided methods return null. but here, the class RedisCacheApi is never used.

Greg Methvin

unread,
Jan 31, 2016, 9:48:06 AM1/31/16
to play-framework
There is already a redis cache implementation for Play. Did you try that?

If you end up deciding to write your own cache plugin that's a pretty good example project, too.

...

Thibault Meyer

unread,
Jan 31, 2016, 11:55:11 AM1/31/16
to play-framework
This implementation is developped on Scala, it not solve the issue on Java version of PlayFramework. As I says, the Scala version of Play.Cache customization works, not the Java version.

Greg Methvin

unread,
Jan 31, 2016, 12:11:03 PM1/31/16
to play-framework
Can you share the code for your module? Are you actually binding a play.cache.CacheApi somewhere?

The implementation I linked does bind a Java CacheApi so it should work. Do you get the same error when using it?

_____________________________
From: Thibault Meyer <meyer.t...@gmail.com>
Sent: Sunday, January 31, 2016 8:55 AM
Subject: [play-framework] Re: Play 2.4.6 - Custom play cache implementation not working on Java
To: play-framework <play-fr...@googlegroups.com>
--
You received this message because you are subscribed to a topic in the Google Groups "play-framework" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/play-framework/XwjfFumb6nU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/021044e1-2024-4abc-b66e-e61f1163e104%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


Thibault Meyer

unread,
Jan 31, 2016, 1:15:12 PM1/31/16
to play-framework
Here the module.  The Logger.info("defaultCachhe: {}, defaultCacheName) print on the console this sentence : "defaultCache: play"


package modules;

import play.Logger;
import play.api.Configuration;
import play.api.Environment;
import play.api.inject.Binding;
import play.api.inject.Module;
import play.cache.CacheApi;
import play.cache.NamedCacheImpl;
import scala.collection.Seq;

import javax.inject.Singleton;

public class RedisCacheModule extends Module {

@Override
public Seq<Binding<?>> bindings(final Environment environment, final Configuration configuration) {
final String defaultCacheName = configuration.underlying().getString("play.cache.defaultCache");
Logger.info("defaultCache: {}", defaultCacheName);
return seq(
bind(CacheApi.class).qualifiedWith(new NamedCacheImpl(defaultCacheName)).to(RedisCacheApi.class).in(Singleton.class)
);
}
}


Thibault Meyer

unread,
Jan 31, 2016, 1:17:12 PM1/31/16
to play-framework
I can if you want create a proplay project you can compile on your computer ?

Greg Methvin

unread,
Jan 31, 2016, 1:44:37 PM1/31/16
to play-framework
The one thing I notice is that you only are binding the cache with the qualifier annotation (i.e. it will only work if you have @NamedCache("play")). Is that intentional? If you only need one cache, you should just bind(CacheApi.class).to(RedisCacheApi.class).in(Singleton.class)

Other than that I'm not sure. If that log message is displaying it's clearly installing your module.

I would still recommend trying the module from Typesafe, though, unless there is something really custom you're looking to do.

Reply all
Reply to author
Forward
0 new messages