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

Jackson in native image failed to deserialize JsonNode into ConcurrentHashMap with generics #4400

Closed
zipper01 opened this issue Feb 25, 2024 · 6 comments

Comments

@zipper01
Copy link

zipper01 commented Feb 25, 2024

The deserializer is claimed as @JsonDeserialize(using = Subscriber.Deserializer.class) and defined as below, which works perfectly in a jar file running with Java -jar:

    public static class Deserializer extends StdDeserializer<Subscriber> {

        public Deserializer() {
            //super(Subscriber.class);
            this(null);
        }

        public Deserializer(Class<?> vc) {
            super(vc);
        }

        @Override
        public Subscriber deserialize(JsonParser jp, DeserializationContext ctx)
                throws IOException, JsonProcessingException {
            JsonNode node = jp.getCodec().readTree(jp);
            if (node.isEmpty()) {
                return null;
            }

            JsonNode v = node.get("statusId");
            int id = v == null ? -1 : v.asInt(-1);
            v = node.get("name");
            String name = v == null ? null : v.asText(null);
            v = node.get("password");
            String password = v == null ? null : v.asText(null);
            ConcurrentHashMap<Integer, Account> accounts = null;
            v = node.get("accounts");
            if (v != null) {
                accounts = (new ObjectMapper()).convertValue(v,
                        new TypeReference<ConcurrentHashMap<Integer, Account>>() {
                });
            }
            return new Subscriber(id, name, password, accounts);
        }
    }

But after I converted the jar file to a native image using GraalVM's native-image tool and run the native image, it threw the below exception (at line convertValue) at run-time:

java.lang.IllegalArgumentException: Cannot construct instance of `java.util.concurrent.ConcurrentHashMap` (no Creators, like default constructor, exist): no default constructor found
 at [Source: UNKNOWN; byte offset: #UNKNOWN]
        at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4618)
        at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:4559)
        at myclient.sub.Subscriber$Deserializer.deserialize(Subscriber.java:71)
        at myclient.sub.Subscriber$Deserializer.deserialize(Subscriber.java:43)
        at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4613)
        at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:4549)
        at myclient.AgencyMgr.onCtrlLoginSucceeded(AgencyMgr.java:345)
        at myclient.AgencyMgr.lambda$ctrlLogin$4(AgencyMgr.java:338)
        at myclient.net.MyClientEndpoint.runBlockingCtrlOperation(MyClientEndpoint.java:271)
        at myclient.AgencyMgr.ctrlLogin(AgencyMgr.java:333)
        at myclient.AgencyMgr.login(AgencyMgr.java:240)
        at myclient.ui.UIMgr.lambda$new$2(UIMgr.java:26)
        at myclient.ui.Tui$1.run(Tui.java:93)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:833)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.util.concurrent.ConcurrentHashMap` (no Creators, like default constructor, exist): no default constructor found
 at [Source: UNKNOWN; byte offset: #UNKNOWN]
        at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1887)
        at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414)
        at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1375)
        at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:439)
        at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:32)
        at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4613)
        ... 14 more

I tried to use MapType type = jsonObjectMapper.getTypeFactory().constructMapType(ConcurrentHashMap.class, Integer.class, Account.class) instead of TypeReference and it resulted in the same.
The jackson.core version is 2.16.1 and:
native-image --version
native-image 21.0.2 2024-01-16
GraalVM Runtime Environment Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS-jvmci-23.1-b30)
Substrate VM Oracle GraalVM 21.0.2+13.1 (build 21.0.2+13-LTS, serial gc, compressed references)

I posted this to StackOverflow and got no correct answer and I realized it may need to change the Jackson Jason lib.

@cowtowncoder
Copy link
Member

Wrong repo, will move to jackson-databind.

@cowtowncoder cowtowncoder transferred this issue from FasterXML/jackson-core Feb 26, 2024
@cowtowncoder
Copy link
Member

It sounds like this might be same as #4299 ? If so, will be addressed in 2.17.0 release.

@zipper01
Copy link
Author

zipper01 commented Feb 28, 2024

It sounds like this might be same as #4299 ? If so, will be addressed in 2.17.0 release.

I'm not sure but the exceptions are similar. In my code I'm using plain Java accounts = (new ObjectMapper()).convertValue(v, new TypeReference<ConcurrentHashMap<Integer, Account>>() {}); and the exception clearly points to ObjectMapper._convert. In #4299 reproducer (https://github.com/edudar/feign-native), client.getData() should reference the code public record Data(String id, String name, Set<String> sets, List<String> lists, Map<String, String> maps) {} in Data.java; but I don't know if the exact method triggering the exception is the same.

@cowtowncoder
Copy link
Member

cowtowncoder commented Feb 28, 2024 via email

@zipper01
Copy link
Author

zipper01 commented Feb 29, 2024

@cowtowncoder - I tried 2.17.0-rc1 and it worked, the issue is resolved. Thank you and the team!

@cowtowncoder
Copy link
Member

@zipper01 Thank you for verifying!

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

No branches or pull requests

2 participants