EsotericSoftware / kryo

Java binary serialization and cloning: fast, efficient, automatic
BSD 3-Clause "New" or "Revised" License
6.19k stars 827 forks source link

Deserialize fails :"Unable to find class: void" when sets registrationRequired to false in kryo 5.0.0-RC5-SNAPSHOT #702

Closed Warkeeper closed 4 years ago

Warkeeper commented 4 years ago

Environment

Steps to reproduce this issue

  1. set registrationRequired flag to false when initialize kryo
  2. serialize a object which cotains field 'void.class'
  3. deserialize the object

Expected Result

Nothing goes wrong, I got the proper deserialized object.

Actual Result

An exception is thrown when kryo is reading object.

Exception trace:

Exception in thread "main" com.esotericsoftware.kryo.KryoException: Unable to find class: void
Serialization trace:
voidType (com.unionpay.test.kryo.KryoTest$SomeClass)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readName(DefaultClassResolver.java:182)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readClass(DefaultClassResolver.java:151)
    at com.esotericsoftware.kryo.Kryo.readClass(Kryo.java:677)
    at com.esotericsoftware.kryo.serializers.DefaultSerializers$ClassSerializer.read(DefaultSerializers.java:293)
    at com.esotericsoftware.kryo.serializers.DefaultSerializers$ClassSerializer.read(DefaultSerializers.java:282)
    at com.esotericsoftware.kryo.Kryo.readObjectOrNull(Kryo.java:773)
    at com.esotericsoftware.kryo.serializers.ReflectField.read(ReflectField.java:120)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:122)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:696)
    at com.unionpay.test.kryo.KryoTest.main(KryoTest.java:25)
Caused by: java.lang.ClassNotFoundException: void
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at com.esotericsoftware.kryo.util.DefaultClassResolver.readName(DefaultClassResolver.java:176)
    ... 9 more

Codes to reproduce this issue

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class KryoTest {
    public static void main(String[] args) throws Exception {
        Kryo kryo = new Kryo();

        kryo.setRegistrationRequired(false);

        SomeClass object = new SomeClass();

        Output output = new Output(new FileOutputStream("file.bin"));
        kryo.writeObject(output, object);
        output.close();

        Input input = new Input(new FileInputStream("file.bin"));
        SomeClass object2 = kryo.readObject(input, SomeClass.class);
        input.close();
    }

    static public class SomeClass {
        Class voidType = void.class;
    }
}
Warkeeper commented 4 years ago

BTW, It works fine if I register void.class Class.class SomeClass.class.

theigl commented 4 years ago

This also works if you use Void.class and not the primitive void.class. Is that an option in your case?

I quickly checked the code and it is possible to fix this but not without breaking serialization compatibility.

Warkeeper commented 4 years ago

Thanks for reply. @theigl It's difficult to use Void.class instead of void.class in my case because I'm using kryo in a RPC framework and void.class may appear in user's return type (or even in arguments) which framework can not control. Is there any possibility to fix it without breaking compatibility ? I could work around it in my framework, but I think it may be better if kryo could solve it.

theigl commented 4 years ago

@Warkeeper: I just checked again. Fixing this is currently only possible by registering a new serializer for the primitive void and that breaks backwards compatibility because class registration IDs change. This is something that could be added in the next major release.

For now, please work around this by registering void.class manually.

magro commented 4 years ago

Isn't this the way kryo expects to deal with application specific requirements? So while kryo could register it by default, it's ok and not a workaround if that's done here by the application.

theigl commented 4 years ago

That's true, but all other primitive types are registered automatically: https://github.com/EsotericSoftware/kryo/blob/ac091570e43ea74891ca297fc8414f4b08190855/src/com/esotericsoftware/kryo/Kryo.java#L230

Not registering void by default could be intentional because it is typically not used a lot, but it could also be an oversight.

magro commented 4 years ago

Ok. Closed for now, please reopen if this is still an issue.