Skip to content

Commit bf510c2

Browse files
committed
extension point for ExecutionStrategy
This will allows to customize which java.util.Map concrete class to use. For example, eclipse-collections has Map implementations with better memory footprint than JDK maps.
1 parent a25f93b commit bf510c2

File tree

3 files changed

+89
-18
lines changed

3 files changed

+89
-18
lines changed

src/main/java/graphql/execution/AbstractAsyncExecutionStrategy.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,4 @@ public AbstractAsyncExecutionStrategy(DataFetcherExceptionHandler dataFetcherExc
2121
super(dataFetcherExceptionHandler);
2222
}
2323

24-
protected BiConsumer<List<Object>, Throwable> handleResults(ExecutionContext executionContext, List<String> fieldNames, CompletableFuture<ExecutionResult> overallResult) {
25-
return (List<Object> results, Throwable exception) -> executionContext.run(exception, () -> {
26-
if (exception != null) {
27-
handleNonNullException(executionContext, overallResult, exception);
28-
return;
29-
}
30-
31-
Map<String, Object> resolvedValuesByField = Maps.newLinkedHashMapWithExpectedSize(fieldNames.size());
32-
int ix = 0;
33-
for (Object result : results) {
34-
String fieldName = fieldNames.get(ix++);
35-
resolvedValuesByField.put(fieldName, result);
36-
}
37-
overallResult.complete(new ExecutionResultImpl(resolvedValuesByField, executionContext.getErrors()));
38-
});
39-
}
4024
}

src/main/java/graphql/execution/ExecutionStrategy.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,12 @@ private BiConsumer<List<Object>, Throwable> buildFieldValueMap(List<String> fiel
291291
};
292292
}
293293

294-
@NonNull
295-
private static Map<String, Object> buildFieldValueMap(List<String> fieldNames, List<Object> results) {
294+
/**
295+
* Default implementation that creates a mutable {@link java.util.LinkedHashMap}.
296+
* Concrete classes are enabled to override this method to change the concrete {@link Map} implementation
297+
* (e.g. immutable maps, unified maps from eclipse-collections and so on).
298+
*/
299+
protected Map<String, Object> buildFieldValueMap(List<String> fieldNames, List<Object> results) {
296300
Map<String, Object> resolvedValuesByField = Maps.newLinkedHashMapWithExpectedSize(fieldNames.size());
297301
int ix = 0;
298302
for (Object fieldValue : results) {
@@ -1135,6 +1139,17 @@ protected ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionCo
11351139
.build();
11361140
}
11371141

1142+
protected BiConsumer<List<Object>, Throwable> handleResults(ExecutionContext executionContext, List<String> fieldNames, CompletableFuture<ExecutionResult> overallResult) {
1143+
return (List<Object> results, Throwable exception) -> {
1144+
if (exception != null) {
1145+
handleNonNullException(executionContext, overallResult, exception);
1146+
return;
1147+
}
1148+
final var map = buildFieldValueMap(fieldNames, results);
1149+
overallResult.complete(new ExecutionResultImpl(map, executionContext.getErrors()));
1150+
};
1151+
}
1152+
11381153
@NonNull
11391154
private static Supplier<ImmutableMapWithNullValues<String, Object>> getArgumentValues(ExecutionContext executionContext,
11401155
List<GraphQLArgument> fieldArgDefs,
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package graphql
2+
3+
import graphql.execution.AsyncExecutionStrategy
4+
import graphql.schema.idl.RuntimeWiring
5+
import groovy.transform.Immutable
6+
import spock.lang.Specification
7+
8+
class CustomMapImplementation extends Specification {
9+
10+
@Immutable
11+
static class Person {
12+
String name
13+
@SuppressWarnings('unused') // used by graphql-java
14+
int age
15+
}
16+
17+
class CustomMapExecutionStrategy extends AsyncExecutionStrategy {
18+
19+
@Override
20+
protected Map<String, Object> buildFieldValueMap(List<String> fieldNames, List<Object> results) {
21+
var mutable = super.buildFieldValueMap(fieldNames, results)
22+
// just change the Map to be immutable
23+
// real world implementation could use eclipse-collections unified maps that are 50% smaller than JDK maps
24+
return Collections.unmodifiableMap(mutable)
25+
}
26+
}
27+
28+
def graphql = TestUtil.graphQL("""
29+
type Query {
30+
people: [Person!]!
31+
}
32+
33+
type Person {
34+
name: String!
35+
age: Int!
36+
}
37+
38+
""",
39+
RuntimeWiring.newRuntimeWiring()
40+
.type("Query", {
41+
it.dataFetcher("people", { List.of(new Person("Mario", 18), new Person("Luigi", 21))})
42+
})
43+
.build())
44+
.queryExecutionStrategy(new CustomMapExecutionStrategy())
45+
.build()
46+
47+
def "customMapImplementation"() {
48+
when:
49+
def input = ExecutionInput.newExecutionInput()
50+
.query('''
51+
query {
52+
people {
53+
name
54+
age
55+
}
56+
}
57+
''')
58+
.build()
59+
60+
def executionResult = graphql.execute(input)
61+
62+
then:
63+
executionResult.errors.isEmpty()
64+
executionResult.data == [ people: [
65+
[name: "Mario", age: 18],
66+
[name: "Luigi", age: 21],
67+
]]
68+
executionResult.data.getClass().getSimpleName() == 'UnmodifiableMap'
69+
executionResult.data['people'].each { it -> it.getClass().getSimpleName() == 'UnmodifiableMap' }
70+
}
71+
72+
}

0 commit comments

Comments
 (0)