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: byte[] for serialized valued. #176

Closed
vahid-01 opened this issue Jul 2, 2018 · 5 comments
Closed

ClassNotFoundException: byte[] for serialized valued. #176

vahid-01 opened this issue Jul 2, 2018 · 5 comments
Labels
Milestone

Comments

@vahid-01
Copy link

vahid-01 commented Jul 2, 2018

Hi. I have some problem with play-redis and I think this is a bug.
This Plugin is work fine if we didn't use custom serialization.
This is our scenario. The value serialized before set and deserialized after get.
The value after serialization is byte[] and the ClassTag stored in Redis is byte[].
When we called get we got this exception:

Caused by: java.lang.ClassNotFoundException: byte[]
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:466)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:566)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
	at play.api.cache.redis.impl.JavaRedis.decodeClassTag$1(JavaRedis.scala:60)
	at play.api.cache.redis.impl.JavaRedis.$anonfun$getOrElse$1(JavaRedis.scala:61)
	at scala.Option.map(Option.scala:146)
	at play.api.cache.redis.impl.JavaRedis.decodedClassTag$1(JavaRedis.scala:61)
	at play.api.cache.redis.impl.JavaRedis.$anonfun$getOrElse$2(JavaRedis.scala:63)
	at scala.util.Success.$anonfun$map$1(Try.scala:251)
	at scala.util.Success.map(Try.scala:209) 

If the byte[] is incorrect type for value, this method should be fix :


  def set( key: String, value: scala.Any, duration: Duration ): Future[ Done ] = {
    import dsl._

    Future.sequence(
      Seq(
        // set the value
        internal.set( key, value, duration ),
        // and set its type to be able to read it
        internal.set( s"classTag::$key", if ( value == null ) "" else value.getClass.getCanonicalName, duration )
      )
    ).map {
      case Seq( done, _ ) => done
    }
  }

Maybe if you define another set method that get class type our problem resolved!

Version of play-redis = 2.1.2
Version of Scala = 2.12.6
Version of Play = 2.6.15

@KarelCemus
Copy link
Owner

I am not sure I understand your issue. Can you implement a test to reproduce it?

I sounds like deserialization does not support type byte[].

The value serialized before set and deserialized after get.

Why? Akka provides a mechanism for custom serialization, why don't you use it?

@vahid-01
Copy link
Author

vahid-01 commented Jul 3, 2018

Thanks for reply. We have custom Kryo serialization that work fine. I found akka kryo serialization that implement Kryo for akka but this plugin has bug for multi threading. So we have 2 option: 1- implement custom Kryo serialization for akka. 2- used old custom Kryo serialization with above scenario.

@KarelCemus
Copy link
Owner

What about this issue? Can you confirm the cause? I am not sure I understand it correctly.

And regarding Kryo, you can use explicit strategy instead of incremental. It works nice, I use it in tens of projects.

@vahid-01
Copy link
Author

vahid-01 commented Jul 3, 2018

I write simple module in play that run this code :

package services;

import play.Logger;
import play.cache.SyncCacheApi;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * @author vahid
 */
@Singleton
public class KryoBindService {

    private final SyncCacheApi syncCacheApi;
    protected final Logger.ALogger logger = Logger.of(getClass());


    @Inject
    public KryoBindService(SyncCacheApi syncCacheApi) {
        this.syncCacheApi = syncCacheApi;
        logger.info("start get and set byte[] to play redis");

        try {


            syncCacheApi.set("string", "vahid");
            System.out.println("string:" + syncCacheApi.get("string"));

            syncCacheApi.set("bytes", new byte[0]);
            System.out.println("bytes:" + String.valueOf(syncCacheApi.get("bytes")));

            logger.info("finish get and set byte[] to play redis");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

and got this error :

[info] 2018-07-03 14:13:18,099 s.KryoBindService - start get and set byte[] to play redis
string:vahid
java.lang.RuntimeException: java.util.concurrent.ExecutionException: java.lang.ClassNotFoundException: byte[]
	at play.cache.DefaultSyncCacheApi.blocking(DefaultSyncCacheApi.java:92)
	at play.cache.DefaultSyncCacheApi.get(DefaultSyncCacheApi.java:35)
	at services.KryoBindService.<init>(KryoBindService.java:31)
	at services.KryoBindService$$FastClassByGuice$$be9a370b.newInstance(<generated>)
	at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:89)
	at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:111)
	at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:90)
	at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:268)
	at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)
	at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1092)
	at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
	at com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:194)
	at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:41)
	at com.google.inject.internal.InternalInjectorCreator$1.call(InternalInjectorCreator.java:205)
	at com.google.inject.internal.InternalInjectorCreator$1.call(InternalInjectorCreator.java:199)
	at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1085)
	at com.google.inject.internal.InternalInjectorCreator.loadEagerSingletons(InternalInjectorCreator.java:199)
	at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:180)
	at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:110)
	at com.google.inject.Guice.createInjector(Guice.java:99)
	at com.google.inject.Guice.createInjector(Guice.java:84)
	at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:185)
	at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:137)
	at play.api.inject.guice.GuiceApplicationLoader.load(GuiceApplicationLoader.scala:21)
	at play.core.server.DevServerStart$$anon$1.$anonfun$reload$3(DevServerStart.scala:174)
	at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
	at play.core.server.DevServerStart$$anon$1.reload(DevServerStart.scala:171)
	at play.core.server.DevServerStart$$anon$1.get(DevServerStart.scala:124)
	at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:222)
	at play.core.server.AkkaHttpServer.$anonfun$createServerBinding$1(AkkaHttpServer.scala:137)
	at akka.stream.impl.fusing.MapAsync$$anon$25.onPush(Ops.scala:1194)
	at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:519)
	at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:411)
	at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:585)
	at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:469)
	at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:560)
	at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:742)
	at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:757)
	at akka.actor.Actor.aroundReceive(Actor.scala:517)
	at akka.actor.Actor.aroundReceive$(Actor.scala:515)
	at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:667)
	at akka.actor.ActorCell.receiveMessage(ActorCell.scala:590)
	at akka.actor.ActorCell.invoke(ActorCell.scala:559)
	at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257)
	at akka.dispatch.Mailbox.run(Mailbox.scala:224)
	at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
	at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
	at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
	at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
	at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: java.util.concurrent.ExecutionException: java.lang.ClassNotFoundException: byte[]
	at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
	at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2022)
	at scala.concurrent.java8.FuturesConvertersImpl$CF.super$get(FutureConvertersImpl.scala:84)
	at scala.concurrent.java8.FuturesConvertersImpl$CF.$anonfun$get$2(FutureConvertersImpl.scala:84)
	at akka.dispatch.MonitorableThreadFactory$AkkaForkJoinWorkerThread$$anon$3.block(ThreadPoolBuilder.scala:167)
	at akka.dispatch.forkjoin.ForkJoinPool.managedBlock(ForkJoinPool.java:3641)
	at akka.dispatch.MonitorableThreadFactory$AkkaForkJoinWorkerThread.blockOn(ThreadPoolBuilder.scala:165)
	at scala.concurrent.package$.blocking(package.scala:142)
	at scala.concurrent.java8.FuturesConvertersImpl$CF.get(FutureConvertersImpl.scala:84)
	at play.cache.DefaultSyncCacheApi.blocking(DefaultSyncCacheApi.java:86)
	... 49 more
Caused by: java.lang.ClassNotFoundException: byte[]
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:466)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:566)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
	at play.api.cache.redis.impl.JavaRedis.decodeClassTag$1(JavaRedis.scala:60)
	at play.api.cache.redis.impl.JavaRedis.$anonfun$getOrElse$1(JavaRedis.scala:61)
	at scala.Option.map(Option.scala:146)
	at play.api.cache.redis.impl.JavaRedis.decodedClassTag$1(JavaRedis.scala:61)
	at play.api.cache.redis.impl.JavaRedis.$anonfun$getOrElse$2(JavaRedis.scala:63)
	at scala.util.Success.$anonfun$map$1(Try.scala:251)
	at scala.util.Success.map(Try.scala:209)
	at scala.concurrent.Future.$anonfun$map$1(Future.scala:288)
	at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:29)
	at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:29)
	at scala.concurrent.impl.CallbackRunnable.run$$$capture(Promise.scala:60)
	at scala.concurrent.impl.CallbackRunnable.run(Promise.scala)
	at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:56)
	at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:844)

The"classTag::bytes" for bytes key is byte[].
The error is exist.
I used incremental and default strategy and the problem exist for Kryo.

@KarelCemus
Copy link
Owner

Well, the workaround is quire simple, since you already handle (de)serialization yourself. Just produce a string instead of byte[] as Redis does not support bytes if I'm not wrong. So basically, the fix will serialize bytes probably into base64 or similar and you can do it yourself directly. I believe byte[] is not good data type for caching in redis. I agree that it is a bug but raised from discouraged use.

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

No branches or pull requests

2 participants