[2.1.2 Scala] The play Cache is not alive (STATUS_SHUTDOWN) from EhCache in tests

3,612 views
Skip to first unread message

Johan Andren

unread,
Jul 23, 2013, 9:00:57 AM7/23/13
to play-fr...@googlegroups.com
Trying to enable cache for some stuff in our play app and ran into this error, which i get during test execution inside a WithApplication. Is it impossible to write WithApplication tests against a webapp if it uses the default EhCache plugin?

Johan Andren

unread,
Jul 25, 2013, 7:07:33 AM7/25/13
to play-fr...@googlegroups.com
Ok, turns out to be because of EhCache having this per-JVM-singleton (mentioned by James Roper here: https://groups.google.com/d/msg/play-framework/JrzkjVAV2nU/FnEBzhXvlc4J) to manage caches and that is being shutdown on app stop, which in the tests WithApplication is once per test, so the first test might work but the rest will fail since the CacheManager is shutdown. I'm thinking this may affect multiple run:s in the same play instance as well.

Creating our own cache plugin for now to get this to work. Should I open a bug for this?

angeloh

unread,
Nov 18, 2013, 11:25:19 PM11/18/13
to play-fr...@googlegroups.com

Johan Andren

unread,
Nov 19, 2013, 9:02:32 AM11/19/13
to play-fr...@googlegroups.com
We wrote our own cache plugin that flushes the cache onStop, except for that it is an identical copy of the built in one.

bryan hunt

unread,
Nov 20, 2013, 4:03:09 AM11/20/13
to play-fr...@googlegroups.com

Care to share it with the list ?

Bryan

Johan Andren

unread,
Nov 20, 2013, 9:22:55 AM11/20/13
to play-fr...@googlegroups.com
Here you go!
(Thanks plotagon for letting me share this)

/**
 * Custom in-memory cache plugin working around the shortcomings of the play bundled one.
 *
 * See more:
 *
 */
class FixedEhCachePlugin(app: Application) extends CachePlugin {

  lazy val cache = {
    val manager = CacheManager.getInstance()
    manager.addCacheIfAbsent("play")
    manager.getCache("play")
  }

  override def onStart() {
    cache
  }

  override def onStop() {
    cache.flush()
  }

  lazy val api = new CacheAPI {

    def set(key: String, value: Any, expiration: Int) {
      val element = new Element(key, value)
      if (expiration == 0) element.setEternal(true)
      element.setTimeToLive(expiration)
      cache.put(element)
    }

    def get(key: String): Option[Any] = {
      Option(cache.get(key)).map(_.getObjectValue)
    }

    def remove(key: String) {
      cache.remove(key)
    }
  }

}

Andreas Fürer

unread,
Feb 11, 2014, 5:27:53 AM2/11/14
to play-fr...@googlegroups.com
We were running into classloading issues with the solution posted in the FixedEhCachePlugin.

If Play recompiles some classes and reloads the play application, we see ClassNotFoundException. The stack trace (see below) indicates that we have a classloading issue related to EhCache.

Our approach now is to do the regular cacheManager.shutdown() in case the Play application runs in dev or prod mode and to keep the cache manager in test mode:

  /* ... */

  override def onStop() {
    if (play.api.Play.isTest(app)) {
      cache.flush()
    } else {
      // shutdown of the cache manager in dev/prod mode
      if (loaded) {
        manager.shutdown()
      }
    }
    Logger.info("Stopped Cache Plugin")
  }


Stacktrace of the symptom described above:

java.lang.ExceptionInInitializerError: null
    at Routes$$anonfun$routes$1$$anonfun$applyOrElse$219$$anonfun$apply$219.apply(routes_routing.scala:3078) ~[na:na]
    at Routes$$anonfun$routes$1$$anonfun$applyOrElse$219$$anonfun$apply$219.apply(routes_routing.scala:3078) ~[na:na]
    at play.core.Router$HandlerInvoker$$anon$5.call(Router.scala:180) ~[play_2.10.jar:2.1.4]
    at play.core.Router$Routes$class.invokeHandler(Router.scala:371) ~[play_2.10.jar:2.1.4]
    at Routes$.invokeHandler(routes_routing.scala:15) ~[na:na]
    at Routes$$anonfun$routes$1$$anonfun$applyOrElse$219.apply(routes_routing.scala:3078) ~[na:na]
Caused by: net.sf.ehcache.CacheException: java.lang.ClassNotFoundException: com.obfuscated.package.geo.AdministrativeArea
    at net.sf.ehcache.store.disk.DiskStorageFactory.retrieve(DiskStorageFactory.java:916) ~[ehcache-core.jar:na]
    at net.sf.ehcache.store.disk.Segment.decodeHit(Segment.java:183) ~[ehcache-core.jar:na]
    at net.sf.ehcache.store.disk.Segment.get(Segment.java:221) ~[ehcache-core.jar:na]
    at net.sf.ehcache.store.disk.DiskStore.get(DiskStore.java:489) ~[ehcache-core.jar:na]
    at net.sf.ehcache.store.disk.DiskStore.getQuiet(DiskStore.java:496) ~[ehcache-core.jar:na]
    at net.sf.ehcache.store.FrontEndCacheTier.getQuiet(FrontEndCacheTier.java:230) ~[ehcache-core.jar:na]
Caused by: java.lang.ClassNotFoundException: com.obfuscated.package.geo.AdministrativeArea
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366) ~[na:1.7.0_51]
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355) ~[na:1.7.0_51]
    at java.security.AccessController.doPrivileged(Native Method) ~[na:1.7.0_51]
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354) ~[na:1.7.0_51]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425) ~[na:1.7.0_51]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358) ~[na:1.7.0_51]

Niklas Nylund

unread,
Feb 16, 2014, 7:39:34 AM2/16/14
to play-fr...@googlegroups.com
We are having the same issue but in java, adding play.Play.application().plugin(EhCachePlugin.class).cache().flush(); seems to fix it. IMHO this shouldn't really be needed. Is there a bug tracking this?


Niklas Nylud

Ian Kent

unread,
Feb 16, 2014, 12:38:53 PM2/16/14
to play-fr...@googlegroups.com
There's a relatively simple fix, depending on how you're using WithApplication

If you're using WithApplication on its own, you can do something like this:

If you're already using a FakeApplication object, you can do something like this:

This gist shows the difference between one which always fails and one which always passes:

Anjan

unread,
Mar 31, 2014, 12:56:15 AM3/31/14
to play-fr...@googlegroups.com
I spent some time around the suggestion posted by Ian but could not get it to work. I am still getting the error : 

 IllegalStateException: The play Cache is not alive (STATUS_SHUTDOWN) (Cache.java:4267) 

I have my example setup here : 

https://github.com/anjanpathak/Angularjs-Auth

This is just a copy of https://github.com/mariussoutier/PlayBasics/tree/master/AngularJS-Auth but with a ApplicationSpec test.

The test however does run if I change the the following at https://github.com/anjanpathak/Angularjs-Auth/blob/master/app/controllers/Application.scala

 implicit val app: play.api.Application = play.api.Play.current

to 

 implicit def app: play.api.Application = play.api.Play.current

But I am worried as this could lead to a performance hit. The app will be evaluated every time this code is executed which potentially could be for every request in my case. 

Cheers,

Anjan


Ronny Unger

unread,
May 23, 2014, 11:10:36 AM5/23/14
to play-fr...@googlegroups.com
Your suggested change works out perfectly! Thank you very much!

If you don't mind, please explain why it is working :-)
Reply all
Reply to author
Forward
0 new messages