Skip to content

Java classes with Optional constructor args for GraphQL input types fail with InaccessibleObjectException #470

Closed
@nilshartmann

Description

@nilshartmann

Hi,

I noticed an error (bug?) when using java.lang.Optional on classes for Input-Types in Java.

As a simple example, please see the following schema, that contains nullable input types and fields:

input ContactInput {
    phone: String
}

input AccountInput {
    id: String!

    # nullable, In Java: Optional<String>
    fullname: String

    # nullable, In Java: Optional<ContactInput>
    contact: ContactInput
}

type Mutation {
    createAccount(input: AccountInput!): String!
}

In Java, the AccountInput is represented by a Java Record that contains two fields wrapped in java.util.Optional:

record ContactInput(String phone) {}

record AccountInput(String id,
                      Optional<String> fullname,
                      Optional<ContactInput> contact) { }
 
@MutationMapping
public String createAccount(@Argument AccountInput input) {
  // ...
} 

When I run the mutation and do not specify the contact field everything is fine:

mutation {
  createAccount(input:{
    id: "1",
    fullname: "Klaus"
  })
}

But as soon as I add the contact field to my Mutation...

mutation {
  createAccount(input:{
    id: "1",
    fullname: "Klaus",
  
    contact: {
      phone: "123"
    }
  })
}

...I receive the following error:

2022-08-21 15:55:33.277 ERROR 33878 --- [o-31080-exec-10] s.g.e.ExceptionResolversExceptionHandler : Unresolved InaccessibleObjectException for executionId 117b4274-4eee-e636-3f0e-49d32e34f290

java.lang.reflect.InaccessibleObjectException: Unable to make private java.util.Optional(java.lang.Object) accessible: module java.base does not "opens java.util" to unnamed module @18d87d80
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
	at java.base/java.lang.reflect.Constructor.checkCanSetAccessible(Constructor.java:188) ~[na:na]
	at java.base/java.lang.reflect.Constructor.setAccessible(Constructor.java:181) ~[na:na]
	at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:202) ~[spring-core-5.3.22.jar:5.3.22]
	at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:194) ~[spring-beans-5.3.22.jar:5.3.22]
	at org.springframework.graphql.data.GraphQlArgumentBinder.createValue(GraphQlArgumentBinder.java:304) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.GraphQlArgumentBinder.createValueOrNull(GraphQlArgumentBinder.java:235) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.GraphQlArgumentBinder.createValue(GraphQlArgumentBinder.java:291) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.GraphQlArgumentBinder.bind(GraphQlArgumentBinder.java:163) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.method.annotation.support.ArgumentMethodArgumentResolver.resolveArgument(ArgumentMethodArgumentResolver.java:58) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:83) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.getMethodArgumentValues(DataFetcherHandlerMethod.java:170) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.invoke(DataFetcherHandlerMethod.java:115) ~[spring-graphql-1.0.1.jar:1.0.1]
	at org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer$SchemaMappingDataFetcher.get(AnnotatedControllerConfigurer.java:514) ~[spring-graphql-1.0.1.jar:1.0.1]
...

(Btw: the same error occurs if the Optional does not contain a Record but a regular Class. )

The optional fullname (String) on the other hand seems to make no problems and works as expected.

I built a simple example that demonstrates the problem: https://github.com/nilshartmann/spring-graphql-optional

Not sure, but I think, when Optionals are allowed for primitive types, it should work with own objects too?

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions