EhCache实例名称' play'已经存在

时间:2017-04-05 23:34:51

标签: playframework couchbase ehcache playframework-2.5 playframework-2.6

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

Error in custom provider, play.api.cache.EhCacheExistsException: An EhCache instance with name 'play' already exists.

我发现这不仅适用于 couchdatabase 驱动程序,还适用于其他一些情况,例如https://groups.google.com/forum/#!topic/pac4j-dev/2_EUOCrov7M

6 个答案:

答案 0 :(得分:2)

我找到了一个解决方案 - 在stop hook上强制缓存关闭。它可以在项目中的一个现有模块中完成,如:

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

答案 1 :(得分:1)

如果有人想看一个快速的copypaste修复,这里是我从Andriy Kuba的答案翻译的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())
  }
}

答案 2 :(得分:0)

如果您在运行测试用例时遇到问题(将有多个Applicaiton s),您可以使用SyncCacheApiAsyncCacheApi的虚拟实现并覆盖绑定通过Application

创建provideApplication()
    @Override
    protected Application provideApplication() {
Application application = new GuiceApplicationBuilder().configure(testConfig)
                        .disable(EhCacheModule.class)
    .overrides(bind(SyncCacheApi.class).to(FakeSyncCacheApi.class))
                        .bindings(new MyModule())
                        .in(new File(".")).build();
     return application;
}

,示例FakeSyncCacheApi就像

@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);
    }
}

这里的想法是禁用EhCache模块并拥有我们自己的虚拟实现。

答案 3 :(得分:0)

这是我的基于Andriy Kubas版本的Scala版本,适用于Play 2.5.6。请记住,Play 2.5.3有一个bug,所以stophooks不起作用。

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

}

和当然play.modules.enabled += "modules.CacheModule"

答案 4 :(得分:0)

对我来说,添加一个关闭钩子是行不通的(也许速度不够快,我不知道)。相反,我只是在每个测试套件结束时关闭CacheManager并按顺序运行它们:

override def afterAll(): Unit = {
  CacheManager.getInstance().shutdown()
}

答案 5 :(得分:0)

基于Andriy Kuba答案,我针对Scala和Play 2.6问题的解决方案。

首先,需要在javaCore中包含build.sbt模块才能导入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()