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

objectInputStream.readObject() issue with springboot 3.3.0 + graalvm22 as native image #8929

Closed
KafkaProServerless opened this issue May 15, 2024 · 11 comments
Assignees

Comments

@KafkaProServerless
Copy link

Hello team,

I would like to reach out regarding a small issue.

I have a very simple piece of code in my springboot 3.3 application.

The piece of code is:

 private static Map<?, ?> getNormalizedMap(final String encoded) {
        try (var objectInputStream = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(encoded)))) {
            return (Map<?, ?>) objectInputStream.readObject();
        } catch (IOException | ClassNotFoundException | IllegalArgumentException e) {
            LOGGER.error("encoded={}", encoded, e);
            return Map.of();
        }
    }

This code would run fine on non native image, battle tested in production.

Now, we successfully built a native image.

However, at run time, we would get an issue saying we need to add this java.util.Collections$UnmodifiableMap in this file serialization-config.json, for line: return (Map<?, ?>) objectInputStream.readObject();

We followed the instructions, not knowing if the instructions were correct to begin with.

After following the instructions, we get this error message:

java.io.InvalidClassException: java.util.HashMap; local class incompatible: stream classdesc serialVersionUID = 362498820763181265, local class serialVersionUID = -3563561681480877083
        at java.base@22.0.1/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:598)
        at java.base@22.0.1/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2063)
        at java.base@22.0.1/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1912)
        at java.base@22.0.1/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2237)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1747)
        at java.base@22.0.1/java.io.ObjectInputStream$FieldValues.<init>(ObjectInputStream.java:2603)
        at java.base@22.0.1/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2454)
        at java.base@22.0.1/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2269)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1747)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject(ObjectInputStream.java:525)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject(ObjectInputStream.java:483)

Therefore, I would like to reach out, asking for help on this issue which is happening only with native images.

How to make return (Map<?, ?>) objectInputStream.readObject(); work with native image?

Thank you for your time and good day!

@KafkaProServerless
Copy link
Author

KafkaProServerless commented May 16, 2024

Thanks for looking into this @fernando-valdez

@fernando-valdez
Copy link
Member

Hi @KafkaProServerless, can you please share more details on how you are building the native image?
Are you using the config agent? or are you using the native image gradle/maven plugin?

Also, what is the exact version of GraalVM you are using? If this is not the latest version, can you please try the latest version and share of the problem still happens?

@KafkaProServerless
Copy link
Author

Sure, so, I am using the latest springBoot 3.3.0-RC1. I am building using maven, with the command mvn -Pnative spring-boot:build-image. The graal VM version used is 22.0.1, which I think is very very very recent as of this writing.

Please let me know if I need to provide anything else @fernando-valdez

@fernando-valdez
Copy link
Member

Thanks.
Can you please provide a small project (reproducer) that I can use to reproduce this issue locally? Also, what OS are you using?

@KafkaProServerless
Copy link
Author

Sure, and again thank you for looking into this.

So, here is the setup: I am using ubuntu 22.04.4 LTS

Could you please:

  1. download graalissue.zip and unzip it
  2. cd inside the folder graalissue (which is a very small one file springboot project)
  3. just run mvn clean install and run the jar
  4. at this point, make a http request, something like curl http://localhost:8080/graalissue
  5. you should see this output: "It should be 41 here => 41"

Could you please let me know if you got to this point without issue?
If yes, that would mean the code is working correctly in non native code
If not, I believe it is better to debug a bit before proceeding.

@KafkaProServerless
Copy link
Author

KafkaProServerless commented May 16, 2024

Once done, could you just do perform mvn clean

from here:

  1. please run mvn -Pnative spring-boot:build-image this would create the graalvm native image
  2. If the image is successfully created, can you run docker tag docker.io/library/graalissue:0.0.1-SNAPSHOT graalissue:latest
  3. from there, just run docker run -p 8080:8080 graalissue:latest
  4. same curl again: curl http://localhost:8080/graalissue
  5. and you should see something in the log like:
java.io.InvalidClassException: java.util.HashMap; local class incompatible: stream classdesc serialVersionUID = 362498820763181265, local class serialVersionUID = -3563561681480877083
        at java.base@22.0.1/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:598)
        at java.base@22.0.1/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2063)
        at java.base@22.0.1/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1912)
        at java.base@22.0.1/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2237)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1747)
        at java.base@22.0.1/java.io.ObjectInputStream$FieldValues.<init>(ObjectInputStream.java:2603)
        at java.base@22.0.1/java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2454)
        at java.base@22.0.1/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2269)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1747)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject(ObjectInputStream.java:525)
        at java.base@22.0.1/java.io.ObjectInputStream.readObject(ObjectInputStream.java:483)

Could you please try this and let me know if you reproduce as well?

@KafkaProServerless
Copy link
Author

graalissue.zip

@KafkaProServerless
Copy link
Author

Please let me know if the example is not clear enough @fernando-valdez

@KafkaProServerless
Copy link
Author

Any update please @fernando-valdez

@fernando-valdez
Copy link
Member

Thanks for your patience, but I don't think this is a direct issue from the native image.
The native image has a closed-world assumption: When using Spring Boot to create native images, a closed-world is assumed, which restricts the dynamic aspects of the application. So serialization can be a sensitive issue here.

The error you see is caused because you are using Java serialization and have two different versions of the same class.

Here is an extract from the java docs:

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java Object Serialization Specification. This specification defines the serialVersionUID of an enum type to be 0L. However, it is strongly recommended that all serializable classes other than enum types explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization.

So you can try to refactor your code to explicitly set the value of the serialVersionUID attribute.

I hope this helps!

@KafkaProServerless
Copy link
Author

KafkaProServerless commented May 22, 2024

Thank you for the answer. May I ask if you managed to reproduce the issue on your side? @fernando-valdez

And are you suggesting I should refactor java.util.Map ?

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

No branches or pull requests

2 participants