Description
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 Optional
s are allowed for primitive types, it should work with own objects too?