Skip to content

ClassUtil fails with java.lang.reflect.InaccessibleObjectException trying to setAccessible on OptionalInt with JDK 17+ #4082

@jaikiran

Description

@jaikiran

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

Please consider the following trivial Java code:

package org.myapp;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.OptionalInt;

public class Main {
    public static void main(final String[] args) throws Exception {
        final ObjectMapper objectMapper = new ObjectMapper();
        final String json = "{ }"; // empty
        final Data data = objectMapper.readValue(json, Data.class);
        System.out.println("Read data: " + data);
    }

    public static class Data {
        private OptionalInt value;

        public Data() {

        }

        public void setValue(OptionalInt i) {
            this.value = i;
        }


        public OptionalInt getValue() {
            return this.value;
        }

        @Override
        public String toString() {
            return "Data[value=" + this.value + "]";
        }
    }
}

When using jackson-databind 2.15.2 and Java version 17 and running this program, it results in:

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make private java.util.OptionalInt() accessible: module java.base does not "opens java.util" to unnamed module @4cf328c3
    at java.lang.reflect.AccessibleObject.throwInaccessibleObjectException (AccessibleObject.java:387)
    at java.lang.reflect.AccessibleObject.checkCanSetAccessible (AccessibleObject.java:363)
    at java.lang.reflect.AccessibleObject.checkCanSetAccessible (AccessibleObject.java:311)
    at java.lang.reflect.Constructor.checkCanSetAccessible (Constructor.java:192)
    at java.lang.reflect.Constructor.setAccessible (Constructor.java:185)
    at com.fasterxml.jackson.databind.util.ClassUtil.checkAndFixAccess (ClassUtil.java:995)
    at com.fasterxml.jackson.databind.deser.impl.CreatorCollector._fixAccess (CreatorCollector.java:278)
    at com.fasterxml.jackson.databind.deser.impl.CreatorCollector.setDefaultCreator (CreatorCollector.java:130)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._addExplicitConstructorCreators (BasicDeserializerFactory.java:438)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory._constructDefaultValueInstantiator (BasicDeserializerFactory.java:293)
    at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findValueInstantiator (BasicDeserializerFactory.java:222)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer (BeanDeserializerFactory.java:262)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer (BeanDeserializerFactory.java:151)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2 (DeserializerCache.java:415)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer (DeserializerCache.java:350)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2 (DeserializerCache.java:264)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer (DeserializerCache.java:244)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer (DeserializerCache.java:142)
    at com.fasterxml.jackson.databind.DeserializationContext.findNonContextualValueDeserializer (DeserializationContext.java:644)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve (BeanDeserializerBase.java:539)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2 (DeserializerCache.java:294)
    at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer (DeserializerCache.java:244)
    at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer (DeserializerCache.java:142)
    at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer (DeserializationContext.java:654)
    at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer (ObjectMapper.java:4956)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose (ObjectMapper.java:4826)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue (ObjectMapper.java:3772)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue (ObjectMapper.java:3740)
    at org.myapp.Main.main (Main.java:10)

So com.fasterxml.jackson.databind.util.ClassUtil is trying to setAccessible() on the private constructor of a JDK class java.util.OptionalInt. One way to solve this issue is to configure the ObjectMapper instance as follows:

objectMapper.configure(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS, false);

However, while looking at the code in com.fasterxml.jackson.databind.util.ClassUtil I noticed that there's a specific logic which tries to not setAccessible() on JDK internal classes here https://github.com/FasterXML/jackson-databind/blob/jackson-databind-2.15.2/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java#L994 which looks like:

if (!isPublic || (evenIfAlreadyPublic && !isJDKClass(declaringClass))) {
      ao.setAccessible(true);
  }

Should that !isJDKClass(declaringClass) be perhaps applied even when evenIfAlreadyPublic is false? Something like:

if (!isJDKClass(declaringClass) && (!isPublic || evenIfAlreadyPublic)) {
      ao.setAccessible(true);
  }

That way, it won't try to access the internal JDK classes and run into these exceptions on Java 17+?

Version Information

2.15.2

Reproduction

No response

Expected behavior

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions