Playframework 名为';播放';已经存在
在使用异步couchdatabasejava驱动程序时,我遇到了使用Play framework默认缓存(EHCache)的问题。热重新加载时播放崩溃,错误为:Playframework 名为';播放';已经存在,playframework,couchbase,ehcache,playframework-2.5,playframework-2.6,Playframework,Couchbase,Ehcache,Playframework 2.5,Playframework 2.6,在使用异步couchdatabasejava驱动程序时,我遇到了使用Play framework默认缓存(EHCache)的问题。热重新加载时播放崩溃,错误为: Error in custom provider, play.api.cache.EhCacheExistsException: An EhCache instance with name 'play' already exists. 我发现这不仅适用于couchdatabase驱动程序,也适用于其他一些场景,例如。我找到了一个解决方
Error in custom provider, play.api.cache.EhCacheExistsException: An EhCache instance with name 'play' already exists.
我发现这不仅适用于couchdatabase驱动程序,也适用于其他一些场景,例如。我找到了一个解决方案-在停止挂钩上强制关闭缓存。可以在项目中现有的一个模块中完成,如:
lifecycle.addStopHook(() -> {
...
CacheManager.getInstance().shutdown();
...
});
还可以创建特殊的“修复”模块:
package fixes;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.google.inject.AbstractModule;
import net.sf.ehcache.CacheManager;
import play.Logger;
import play.Logger.ALogger;
import play.inject.ApplicationLifecycle;
/**
* Fix for the hot reloading cache issue.
* "Error in custom provider, play.api.cache.EhCacheExistsException: An EhCache instance with name 'play' already exists."
*
*/
public class CacheFix extends AbstractModule{
@Override
protected void configure() {
bind(CacheFixInstance.class).asEagerSingleton();
}
}
/**
* Only stop hook
*/
@Singleton
class CacheFixInstance {
private static ALogger logger = Logger.of(CacheFixInstance.class);
@Inject
public CacheFixInstance(ApplicationLifecycle lifecycle) {
lifecycle.addStopHook(() -> {
// Force cache to stop.
CacheManager.getInstance().shutdown();
logger.debug("Cache has been shutdown");
// Nothing to return.
return CompletableFuture.completedFuture(null);
});
}
}
在application.conf
中:
enabled += fixes.CacheFix
如果有人想快速复制粘贴修复,这里是我从安德烈·库巴的答案翻译过来的scala版本
package utils
import javax.inject.{Inject, Singleton}
import com.google.inject.AbstractModule
import net.sf.ehcache.CacheManager
import play.api.Logger
import play.api.inject.ApplicationLifecycle
import scala.concurrent.{ExecutionContext, Future}
/**
* Fix for the hot reloading cache issue.
* "Error in custom provider, play.api.cache.EhCacheExistsException: An EhCache instance with name 'play' already exists."
*
*/
class CacheHotReloadFix extends AbstractModule {
override protected def configure(): Unit = {
bind(classOf[CacheHotReloadFixInstance]).asEagerSingleton()
}
}
@Singleton
class CacheHotReloadFixInstance @Inject() (lifecycle: ApplicationLifecycle, implicit val executionContext: ExecutionContext) {
private val logger = Logger(this.getClass)
lifecycle.addStopHook { () =>
logger.debug("Forching ehcach to stop before play reloads")
// Force cache to stop.
Future(CacheManager.getInstance().shutdown())
}
}
如果您在运行测试用例时遇到问题(其中将有多个
应用程序),您可以使用SyncCacheApi
和asyncacheapi
的虚拟实现,并通过providedapplication()
在创建应用程序时重写绑定
而Fakesyncachapi的示例
@Singleton
public class FakeSyncCacheApi implements SyncCacheApi {
private LRUMap cache = new LRUMap();
@Override
public <T> T get(String key) {
return (T) cache.get(key);
}
@Override
public <T> T getOrElseUpdate(String key, Callable<T> block, int expiration) {
return getOrElseUpdate(key, block);
}
@Override
public <T> T getOrElseUpdate(String key, Callable<T> block) {
T value = (T) cache.get(key);
if (value == null) {
try {
value = block.call();
} catch (Exception e) {
}
cache.put(key, value);
}
return value;
}
@Override
public void set(String key, Object value, int expiration) {
cache.put(key, value);
}
@Override
public void set(String key, Object value) {
cache.put(key, value);
}
@Override
public void remove(String key) {
cache.remove(key);
}
}
@Singleton
公共类FakeSyncAcheapi实现SyncCacheApi{
专用LRUMap缓存=新LRUMap();
@凌驾
公共T获取(字符串键){
return(T)cache.get(key);
}
@凌驾
公共T GetOrelsUpdate(字符串键、可调用块、int过期){
返回GetOrelsUpdate(键,块);
}
@凌驾
公共T GetOrelsUpdate(字符串键,可调用块){
T value=(T)cache.get(key);
如果(值==null){
试一试{
value=block.call();
}捕获(例外e){
}
cache.put(键、值);
}
返回值;
}
@凌驾
公共无效集(字符串键、对象值、int过期){
cache.put(键、值);
}
@凌驾
公共无效集(字符串键、对象值){
cache.put(键、值);
}
@凌驾
公共无效删除(字符串键){
缓存。删除(键);
}
}
这里的想法是禁用EhCache
模块,并使用我们自己的虚拟实现 这是我的Scala版本,基于Andrey Kubas版本,该版本适用于Play 2.5.6。记住,游戏2.5.3有一个bug,所以止动钩不起作用
package modules
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import com.google.inject._
import net.sf.ehcache.CacheManager;
import play.api.Logger
import play.api.inject.ApplicationLifecycle
import play.api.{ Configuration, Environment, Mode }
import play.api.inject.{ Module => PlayModule }
class CacheModule extends PlayModule {
def bindings(environment: Environment, configuration: Configuration) = Seq(
bind[CacheFixInstance].toSelf.eagerly
)
}
@Singleton
class CacheFixInstance @Inject()(lifecycle: ApplicationLifecycle) {
val logger = Logger(this.getClass)
lifecycle.addStopHook { () =>
logger.info("CacheInstance stopped")
Future.successful(CacheManager.getInstance().shutdown())
}
}
当然,对于我来说,添加一个关机钩子并没有起作用(可能不够快,我不知道)。相反,我只是在每个测试套件结束时关闭CacheManager并按顺序运行它们:
override def afterAll(): Unit = {
CacheManager.getInstance().shutdown()
}
我对Scala和Play 2.6问题的解决方案基于答案
首先,需要在build.sbt
中包含javaCore
模块,以便能够导入play.api.inject.ApplicationLifecycle
libraryDependencies += javaCore
然后创建一个新类CacheFixInstance.scala
:
package util
import javax.inject.Inject
import net.sf.ehcache.CacheManager
import play.api.Logger
import play.api.inject.ApplicationLifecycle
import scala.concurrent.Future
class CacheFixInstance @Inject()(lifecycle: ApplicationLifecycle) {
private val logger = Logger(getClass)
lifecycle.addStopHook { () =>
logger.info("CacheInstance stopped")
Future.successful(CacheManager.getInstance().shutdown())
}
logger.info(s"Hot reload EHCache fix initialized.")
}
然后将其添加到您的模块配置中,或者交替使用@singleton
注释到CacheFixInstance
类:
bind(classOf[CacheFixInstance]).asEagerSingleton()
我在第2.6.3节中试过了。未修复错误。@nemoo如果您在测试用例中遇到问题,请参阅下面我的答案。在2.5.6中解决。2.5.3有一个错误,因此止动钩无法工作
bind(classOf[CacheFixInstance]).asEagerSingleton()