Playframework 名为';播放';已经存在

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驱动程序,也适用于其他一些场景,例如。我找到了一个解决方

在使用异步couchdatabasejava驱动程序时,我遇到了使用Play framework默认缓存(EHCache)的问题。热重新加载时播放崩溃,错误为:

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()