Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClassNotFoundException when deserializing List[T] from redis #32

Closed
tototoshi opened this issue Mar 8, 2015 · 6 comments · Fixed by #37
Closed

ClassNotFoundException when deserializing List[T] from redis #32

tototoshi opened this issue Mar 8, 2015 · 6 comments · Fixed by #37

Comments

@tototoshi
Copy link
Contributor

I encountered ClassNotFoundException when using memoize.
I'm using scalacache-redis 0.5.2.

This script is minimal example to reproduce the error.

case class User(id: Int, name: String)

object Test {

  import scalacache._
  import memoization._
  import redis._

  implicit val scalaCache = ScalaCache(RedisCache("localhost", 6379))

  // Works fine when the result type is User, but throws Exception when List[User]
  def getUser(id: Int): List[User] = memoize {
    List(User(id, "Taro"))
  }

  def main(args: Array[String]): Unit = {
    getUser(1)
  }

}
scalacachedebug> r
[info] Running Test
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[error] (run-main-3a) java.lang.ClassNotFoundException: User
java.lang.ClassNotFoundException: User
        at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:344)
        at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:626)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1613)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
        at scala.collection.immutable.List$SerializationProxy.readObject(List.scala:477)
        at sun.reflect.GeneratedMethodAccessor7.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:483)
        at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1896)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
        at scalacache.redis.RedisSerialization$class.deserialize(RedisSerialization.scala:57)
        at scalacache.redis.RedisCache.deserialize(RedisCache.scala:13)
        at scalacache.redis.RedisCache$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(RedisCache.scala:30)
        at scalacache.redis.RedisCache$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(RedisCache.scala:30)
        at scala.Option.map(Option.scala:146)
        at scalacache.redis.RedisCache$$anonfun$get$1$$anonfun$apply$1.apply(RedisCache.scala:30)
        at scalacache.redis.RedisCache$$anonfun$get$1$$anonfun$apply$1.apply(RedisCache.scala:28)
        at scala.concurrent.impl.ExecutionContextImpl$DefaultThreadFactory$$anon$2$$anon$4.block(ExecutionContextImpl.scala:48)
        at scala.concurrent.forkjoin.ForkJoinPool.managedBlock(ForkJoinPool.java:3640)
        at scala.concurrent.impl.ExecutionContextImpl$DefaultThreadFactory$$anon$2.blockOn(ExecutionContextImpl.scala:45)
        at scala.concurrent.package$.blocking(package.scala:123)
        at scalacache.redis.RedisCache$$anonfun$get$1.apply(RedisCache.scala:28)
        at scalacache.redis.RedisCache$$anonfun$get$1.apply(RedisCache.scala:28)
        at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
        at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
        at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
        at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
        at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
        at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
        at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
[trace] Stack trace suppressed: run last compile:run for the full output.
java.lang.RuntimeException: Nonzero exit code: 1
        at scala.sys.package$.error(package.scala:27)
[trace] Stack trace suppressed: run last compile:run for the full output.
[error] (compile:run) Nonzero exit code: 1
[error] Total time: 0 s, completed 2015/03/08 22:19:23
127.0.0.1:6379> keys *
1) "Test.getUser(1)"
127.0.0.1:6379> get Test.getUser(1)
"\x05\xac\xed\x00\x05sr\x002scala.collection.immutable.List$SerializationProxy\x00\x00\x00\x00\x00\x00\x00\x01\x03\x00\x00xpsr\x00\x04Userx\xae\xbf\rY%\x97\xf6\x02\x00\x02I\x00\x02idL\x00\x04namet\x00\x12Ljava/lang/String;xp\x00\x00\x00\x01t\x00\x04Tarosr\x00,scala.collection.immutable.ListSerializeEnd$\x8a\\c[\xf7S\x0bm\x02\x00\x00xpx"
@tototoshi
Copy link
Contributor Author

I'm using scalacache-redis with Play 2.3, so ObjectInputStream in scalacache-redis can't see the application.classloader of Play. Need to set custom classloader to RedisCache like play-redis.

@cb372
Copy link
Owner

cb372 commented Mar 8, 2015

Play's classloader magic strikes again. Thanks for investigating.

I'll add a customClassloader argument to RedisCache

class RedisCache(client: Jedis, customClassloader: Option[Classloader] = None)(implicit ...)

and use that in RedisSerialization.

I wonder if memcached has this problem as well. That also uses Java serialization.

By the way, I'd be interested to see how you are integrating ScalaCache with Play. I think a Play plugin might be useful for a lot of people.

@tototoshi
Copy link
Contributor Author

I have not tried but guess scalacache-memcached also has this problem.

play2-memcached plugin do the same thing as play-redis.
https://github.com/mumoshu/play2-memcached/blob/master/plugin/src/main/scala/com/github/mumoshu/play2/memcached/MemcachedPlugin.scala#L80-L86

Writing the backend of Play Cache API with the implementation of scalacache-redis and scalacache-memcached is a good idea. play2-memcached and play-redis don't seem to be good maintained but a lot of people are using them. I hardly use Play Cache API directly, but some popular modules (securesocial, play2-auth...) depend on the API.

cb372 added a commit that referenced this issue Mar 9, 2015
cb372 added a commit that referenced this issue Mar 14, 2015
This is needed when using ScalaCache with Play.

Fixes #32
@cb372 cb372 closed this as completed in #37 Mar 14, 2015
@cb372
Copy link
Owner

cb372 commented Mar 14, 2015

In scalacache-redis 0.6.0 I've added a constructor argument so you can pass in app.classloader and it will be used for deserialization.

While investigating this issue I noticed that scalacache-redis was not thread-safe, so I've fixed that as well.

@tototoshi
Copy link
Contributor Author

👍

@cb372
Copy link
Owner

cb372 commented Mar 19, 2015

An interesting development: this is actually a Scala/Openjdk bug. https://issues.scala-lang.org/browse/SI-9237

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants