2

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

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

4

6 回答 6

4

我想出了一个解决方案 - 在停止挂钩上强制关闭缓存。它可以在您项目中的现有模块之一中完成,例如:

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
于 2017-04-05T23:34:50.983 回答
1

万一在这里快速复制粘贴修复是我从 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())
  }
}
于 2017-07-27T14:49:51.107 回答
0

对我来说,添加关闭挂钩不起作用(可能不够快,我不知道)。相反,我只是在每个测试套件结束时关闭 CacheManager 并按顺序运行它们:

override def afterAll(): Unit = {
  CacheManager.getInstance().shutdown()
}
于 2018-10-05T17:17:13.053 回答
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()
于 2019-11-21T01:08:23.987 回答
0

如果您在运行测试用例时遇到问题(会有多个Applicaitons),您可以在创建via时使用 and 的虚拟实现SyncCacheApiAsyncCacheApi覆盖绑定ApplicationprovideApplication()

    @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模块并拥有我们自己的虚拟实现。

于 2018-01-05T20:08:44.060 回答
0

这是我的基于 Andriy Kubas 版本的 Scala 版本,适用于 Play 2.5.6。请记住,Play 2.5.3 有一个错误,因此 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"

于 2018-03-20T12:37:45.807 回答