Skip to content

Commit 40035bd

Browse files
authored
Merge pull request #614 from graphql-java-kickstart/illigal-access-fix
Do not fail on setAccessible
2 parents 36026c7 + 18bfd51 commit 40035bd

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolverScanner.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import graphql.schema.DataFetchingEnvironment
1212
import org.apache.commons.lang3.ClassUtils
1313
import org.apache.commons.lang3.reflect.FieldUtils
1414
import org.slf4j.LoggerFactory
15+
import java.lang.reflect.AccessibleObject
1516
import java.lang.reflect.Method
1617
import java.lang.reflect.Modifier
1718
import java.lang.reflect.Type
@@ -44,13 +45,13 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
4445
private fun findFieldResolver(field: FieldDefinition, search: Search, scanProperties: Boolean): FieldResolver? {
4546
val method = findResolverMethod(field, search)
4647
if (method != null) {
47-
return MethodFieldResolver(field, search, options, method.apply { isAccessible = true })
48+
return MethodFieldResolver(field, search, options, method.apply(trySetAccessible(field, search.type)))
4849
}
4950

5051
if (scanProperties) {
5152
val property = findResolverProperty(field, search)
5253
if (property != null) {
53-
return PropertyFieldResolver(field, search, options, property.apply { isAccessible = true })
54+
return PropertyFieldResolver(field, search, options, property.apply(trySetAccessible(field, search.type)))
5455
}
5556
}
5657

@@ -61,6 +62,15 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
6162
return null
6263
}
6364

65+
private fun trySetAccessible(field: FieldDefinition, type: JavaType): AccessibleObject.() -> Unit = {
66+
try {
67+
isAccessible = true
68+
} catch (e: RuntimeException) {
69+
log.warn("Unable to make field ${type.unwrap().name}#${field.name} accessible. " +
70+
"Be sure to provide a resolver or open the enclosing module if possible.")
71+
}
72+
}
73+
6474
private fun missingFieldResolver(field: FieldDefinition, searches: List<Search>, scanProperties: Boolean): FieldResolver {
6575
return if (options.allowUnimplementedResolvers || options.missingResolverDataFetcher != null) {
6676
if (options.allowUnimplementedResolvers) {
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package graphql.kickstart.tools
2+
3+
import graphql.ExceptionWhileDataFetching
4+
import graphql.GraphQL
5+
import graphql.execution.AsyncExecutionStrategy
6+
import graphql.schema.GraphQLSchema
7+
import org.junit.Ignore
8+
import org.junit.Test
9+
import java.util.*
10+
11+
/**
12+
* Reflective access to private fields in closed modules is not possible since Java 17.
13+
* When using objects from closed modules in the schema the field resolver scanner will try to access their fields but fail.
14+
* If no other resolver is provided that will result in an [IllegalAccessException]
15+
*/
16+
class InaccessibleFieldResolverTest {
17+
18+
@Test
19+
@Ignore // TODO enable test after upgrading to 17
20+
fun `private field from closed module is not accessible`() {
21+
val schema: GraphQLSchema = SchemaParser.newParser()
22+
.schemaString(
23+
"""
24+
type Query {
25+
locale: Locale
26+
}
27+
28+
type Locale {
29+
country: String!
30+
languageTag: String!
31+
}
32+
""")
33+
.resolvers(Query())
34+
.build()
35+
.makeExecutableSchema()
36+
val gql: GraphQL = GraphQL.newGraphQL(schema)
37+
.queryExecutionStrategy(AsyncExecutionStrategy())
38+
.build()
39+
40+
val result = gql.execute(
41+
"""
42+
query {
43+
locale {
44+
country
45+
languageTag
46+
}
47+
}
48+
"""
49+
)
50+
51+
assertEquals(result.errors.size, 1)
52+
val exceptionWhileDataFetching = result.errors[0] as ExceptionWhileDataFetching
53+
assert(exceptionWhileDataFetching.exception is IllegalAccessException)
54+
}
55+
56+
@Test
57+
fun `private field from closed module is accessible through resolver`() {
58+
val schema: GraphQLSchema = SchemaParser.newParser()
59+
.schemaString(
60+
"""
61+
type Query {
62+
locale: Locale
63+
}
64+
65+
type Locale {
66+
country: String!
67+
languageTag: String!
68+
}
69+
""")
70+
.resolvers(Query(), LocaleResolver())
71+
.build()
72+
.makeExecutableSchema()
73+
val gql: GraphQL = GraphQL.newGraphQL(schema)
74+
.queryExecutionStrategy(AsyncExecutionStrategy())
75+
.build()
76+
77+
val data = assertNoGraphQlErrors(gql) {
78+
"""
79+
query {
80+
locale {
81+
country
82+
languageTag
83+
}
84+
}
85+
"""
86+
}
87+
88+
assertEquals(data["locale"], mapOf("country" to "US", "languageTag" to "en-US"))
89+
}
90+
91+
class Query : GraphQLQueryResolver {
92+
fun locale(): Locale = Locale.US
93+
}
94+
95+
class LocaleResolver : GraphQLResolver<Locale> {
96+
fun languageTag(locale: Locale): String = locale.toLanguageTag()
97+
}
98+
}

0 commit comments

Comments
 (0)