Skip to content

PropertyFetchingImpl findPubliclyAccessibleMethod algorithm cannot find methods on non-public classes implementing public interfaces #4278

Description

@dpolivaev

For example, I get

java.lang.reflect.InaccessibleObjectException: Unable to make public java.lang.Object java.util.TreeMap$Entry.getValue() accessible: module java.base does not "opens java.util" to unnamed module @7c137fd5
        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.Method.checkCanSetAccessible(Method.java:200)
        at java.base/java.lang.reflect.Method.setAccessible(Method.java:194)
        at graphql.schema.PropertyFetchingImpl.findViaSetAccessible(PropertyFetchingImpl.java:306)
        at graphql.schema.PropertyFetchingImpl.lambda$getPropertyValue$3(PropertyFetchingImpl.java:158)
        at graphql.schema.PropertyFetchingImpl.getPropertyViaGetterUsingPrefix(PropertyFetchingImpl.java:216)
        at graphql.schema.PropertyFetchingImpl.getPropertyViaGetterMethod(PropertyFetchingImpl.java:210)
        at graphql.schema.PropertyFetchingImpl.getPropertyValue(PropertyFetchingImpl.java:159)
        at graphql.schema.PropertyDataFetcherHelper.getPropertyValue(PropertyDataFetcherHelper.java:23)
        at graphql.schema.PropertyDataFetcher.getImpl(PropertyDataFetcher.java:131)
        at graphql.schema.PropertyDataFetcher.get(PropertyDataFetcher.java:112)

and it happens because here only super classes but no interface implementations are considered:

 private Method findPubliclyAccessibleMethod(CacheKey cacheKey, Class<?> rootClass, String methodName, boolean dfeInUse, boolean allowStaticMethods) throws NoSuchMethodException {
        Class<?> currentClass = rootClass;
        while (currentClass != null) {
            if (Modifier.isPublic(currentClass.getModifiers())) {
                if (dfeInUse) {
                    //
                    // try a getter that takes singleArgumentType first (if we have one)
                    try {
                        Method method = currentClass.getMethod(methodName, singleArgumentType);
                        if (isSuitablePublicMethod(method, allowStaticMethods)) {
                            METHOD_CACHE.putIfAbsent(cacheKey, new CachedMethod(method));
                            return method;
                        }
                    } catch (NoSuchMethodException e) {
                        // ok try the next approach
                    }
                }
                Method method = currentClass.getMethod(methodName);
                if (isSuitablePublicMethod(method, allowStaticMethods)) {
                    METHOD_CACHE.putIfAbsent(cacheKey, new CachedMethod(method));
                    return method;
                }
            }
            currentClass = currentClass.getSuperclass();
        }
        assert rootClass != null;
        return rootClass.getMethod(methodName);
    }

and the utility tries to use findViaSetAccessible which actually is not necessary e.g. for TreeMap.Entry

    static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;
        V value;
        Entry<K,V> left;
        Entry<K,V> right;
        Entry<K,V> parent;
        boolean color = BLACK;

        /**
         * Make a new cell with given key, value, and parent, and with
         * {@code null} child links, and BLACK color.
         */
        Entry(K key, V value, Entry<K,V> parent) {
            this.key = key;
            this.value = value;
            this.parent = parent;
        }

        /**
         * Returns the key.
         *
         * @return the key
         */
        public K getKey() {
            return key;
        }

        /**
         * Returns the value associated with the key.
         *
         * @return the value associated with the key
         */
        public V getValue() {
            return value;
        }

        /**
         * Replaces the value currently associated with the key with the given
         * value.
         *
         * @return the value associated with the key before this method was
         *         called
         */

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions