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

Serializing UUID not working with java 17 (but same works on java 11) #859

Closed
MrTetrixx opened this issue Sep 28, 2021 · 5 comments
Closed
Labels

Comments

@MrTetrixx
Copy link

We have a project that we are wanted to build with java 17 instead of 11. No code changes - just building java 11 code with java 17. Tests started to fail and it seems the UUID serializer created by kryo does not work when building with java 17. I have added a small reproducer.

To reproduce change java version in the pom.xml to 17 and it will fail with:

Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private final long java.util.UUID.mostSigBits accessible: module java.base does not "opens java.util" to unnamed module @5cb0d902
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
	at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
	at com.esotericsoftware.kryo.serializers.CachedFields.addField(CachedFields.java:123)
	at com.esotericsoftware.kryo.serializers.CachedFields.rebuild(CachedFields.java:99)
	at com.esotericsoftware.kryo.serializers.FieldSerializer.<init>(FieldSerializer.java:82)
	at com.esotericsoftware.kryo.SerializerFactory$FieldSerializerFactory.newSerializer(SerializerFactory.java:124)
	at com.esotericsoftware.kryo.SerializerFactory$FieldSerializerFactory.newSerializer(SerializerFactory.java:108)
	at com.esotericsoftware.kryo.Kryo.newDefaultSerializer(Kryo.java:469)
	at com.esotericsoftware.kryo.Kryo.getDefaultSerializer(Kryo.java:454)
	at com.esotericsoftware.kryo.Kryo.register(Kryo.java:483)
	at Reproducer.<init>(Reproducer.java:21)
	at Reproducer.main(Reproducer.java:41)
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.InputChunked;
import com.esotericsoftware.kryo.io.OutputChunked;
import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy;
import org.objenesis.strategy.StdInstantiatorStrategy;

import java.util.Objects;
import java.util.UUID;

public class Reproducer {
    private static final int BUFFER_SIZE = 16 * 1024; // 16 kB
    private final Kryo kryo;
    private final InputChunked input;
    private final OutputChunked output;

    public Reproducer() {
        kryo = new Kryo();
        kryo.setRegistrationRequired(false);

        kryo.register(UUID.class);

        kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
        input = new InputChunked(BUFFER_SIZE);
        output = new OutputChunked(BUFFER_SIZE);
    }

    public byte[] read(Object value) {
        output.reset();
        kryo.writeClassAndObject(output, value);
        return output.getBuffer();
    }

    public <T> T write(byte[] data){
        input.setBuffer(data);
        //noinspection unchecked
        return (T) kryo.readClassAndObject(input);
    }

    public static void main(String[] args) {
        var k = new Reproducer();
        var original = new MyClass(UUID.randomUUID());
        var serialized = k.write(k.read(original));

        assert original.equals(serialized);
        System.out.println("This works");
    }


    public static class MyClass {
        private final UUID uuid;

        public MyClass(UUID uuid) {
            this.uuid = uuid;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MyClass myClass = (MyClass) o;
            return Objects.equals(uuid, myClass.uuid);
        }

        @Override
        public int hashCode() {
            return Objects.hash(uuid);
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>kryotest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.esotericsoftware</groupId>
            <artifactId>kryo</artifactId>
            <version>5.2.0</version>
        </dependency>
    </dependencies>

</project>

Environment:

  • OS:Ubuntu
  • JDK Version: 11 and 17
@theigl
Copy link
Collaborator

theigl commented Sep 28, 2021

@MrTetrixx: You will need to add the following arguments for now:

--add-opens=java.base/java.util=ALL-UNNAMED

See our current surefire setup for other add-opens statements required to run all Kryo tests on JDK17.

I'll look into improving JDK 17 support in the near future.

@MrTetrixx
Copy link
Author

Ahh ok, yeah we have a bunch of those already but one more won't hurt I guess.

@theigl theigl added question and removed bug labels Dec 11, 2021
@OleksandrVoronin
Copy link

Is there any update on this? I'm running into the same issue when trying to serialize java.io.File, even when using a custom FieldSerializer like so:

    static class FileSerializer extends FieldSerializer<File> {
        public FileSerializer(Kryo kryo, Class<File> type) {
            super(kryo, type);
        }

        @Override
        public void write (Kryo kryo, Output output, File object) {
            output.writeString(object.getPath());
        }

        @Override
        public File read(Kryo kryo, Input input, Class<? extends File> type) {
            return new File(input.readString());
        }
    }

Using --add-opens=java.base/java.io=ALL-UNNAMED as JVM arg works, but is less than ideal.

OS: Windows 11
JDK Version: 16.0.2

@theigl
Copy link
Collaborator

theigl commented Jan 13, 2022

@OleksandrVoronin: I briefly looked into it, but it seems there is not much that can be done. In case of UUID, the serializer needs to access internals via reflection. JDK modules are now encapsulated by default, so users need to open them up for reflection.

Your case does not need reflection though and shouldn't require JVM args. The issue is that you extend FieldSerializer, which reads the declared fields of the target via reflection when it is instantiated. You should extend Serializer<File> instead.

@theigl
Copy link
Collaborator

theigl commented Feb 18, 2022

There is a related issue for java.util.regex.Pattern #885. I think it makes sense to bring custom serializers for both classes over from the kryo-serializers project.

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

No branches or pull requests

3 participants