2
2
3
3
import graphql .ExecutionResult ;
4
4
import graphql .ExecutionResultImpl ;
5
+ import graphql .kickstart .execution .FutureExecutionResult ;
5
6
import graphql .kickstart .execution .GraphQLInvoker ;
6
7
import graphql .kickstart .execution .GraphQLQueryResult ;
7
8
import graphql .kickstart .execution .error .GenericGraphQLError ;
13
14
import java .io .IOException ;
14
15
import java .io .UncheckedIOException ;
15
16
import java .util .Optional ;
17
+ import java .util .concurrent .CancellationException ;
16
18
import java .util .concurrent .CompletableFuture ;
17
19
import java .util .concurrent .CompletionException ;
18
20
import java .util .concurrent .atomic .AtomicReference ;
@@ -40,7 +42,7 @@ public void execute(
40
42
if (request .isAsyncSupported ()) {
41
43
invokeAndHandleAsync (invocationInput , request , response , listenerHandler );
42
44
} else {
43
- invokeAndHandle ( invocationInput , request , response , listenerHandler ).join ();
45
+ handle ( invoke ( invocationInput , request , response ) , request , response , listenerHandler ).join ();
44
46
}
45
47
}
46
48
@@ -54,10 +56,10 @@ private void invokeAndHandleAsync(
54
56
? request .getAsyncContext ()
55
57
: request .startAsync (request , response );
56
58
asyncContext .setTimeout (configuration .getAsyncTimeout ());
57
- AtomicReference <CompletableFuture < Void > > futureHolder = new AtomicReference <>();
59
+ AtomicReference <FutureExecutionResult > futureHolder = new AtomicReference <>();
58
60
AsyncTimeoutListener timeoutListener =
59
61
event -> {
60
- Optional .ofNullable (futureHolder .get ()).ifPresent (it -> it . cancel ( true ) );
62
+ Optional .ofNullable (futureHolder .get ()).ifPresent (FutureExecutionResult :: cancel );
61
63
writeResultResponse (
62
64
invocationInput ,
63
65
GraphQLQueryResult .create (
@@ -68,22 +70,27 @@ private void invokeAndHandleAsync(
68
70
};
69
71
asyncContext .addListener (timeoutListener );
70
72
asyncContext .start (
71
- () ->
72
- futureHolder .set (
73
- invokeAndHandle (invocationInput , request , response , listenerHandler )
74
- .thenAccept (aVoid -> asyncContext .complete ())));
73
+ () -> {
74
+ FutureExecutionResult futureResult = invoke (invocationInput , request , response );
75
+ futureHolder .set (futureResult );
76
+ handle (futureResult , request , response , listenerHandler );
77
+ });
75
78
}
76
79
77
- private CompletableFuture <Void > invokeAndHandle (
78
- GraphQLInvocationInput invocationInput ,
80
+ private CompletableFuture <Void > handle (
81
+ FutureExecutionResult futureResult ,
79
82
HttpServletRequest request ,
80
83
HttpServletResponse response ,
81
84
ListenerHandler listenerHandler ) {
82
- return invoke (invocationInput , request , response )
83
- .thenAccept (it -> writeResultResponse (invocationInput , it , request , response ))
85
+ return futureResult
86
+ .thenApplyQueryResult ()
87
+ .thenAccept (
88
+ it -> writeResultResponse (futureResult .getInvocationInput (), it , request , response ))
84
89
.thenAccept (it -> listenerHandler .onSuccess ())
85
90
.exceptionally (
86
- t -> writeErrorResponse (invocationInput , request , response , listenerHandler , t ))
91
+ t ->
92
+ writeErrorResponse (
93
+ futureResult .getInvocationInput (), request , response , listenerHandler , t ))
87
94
.thenAccept (it -> listenerHandler .onFinally ());
88
95
}
89
96
@@ -106,22 +113,32 @@ private Void writeErrorResponse(
106
113
HttpServletResponse response ,
107
114
ListenerHandler listenerHandler ,
108
115
Throwable t ) {
116
+ Throwable cause = getCause (t );
109
117
if (!response .isCommitted ()) {
110
118
writeResultResponse (
111
- invocationInput , GraphQLQueryResult .create (toErrorResult (t )), request , response );
112
- listenerHandler .onError (t );
119
+ invocationInput , GraphQLQueryResult .create (toErrorResult (cause )), request , response );
120
+ listenerHandler .onError (cause );
113
121
} else {
114
122
log .warn (
115
123
"Cannot write GraphQL response, because the HTTP response is already committed. It most likely timed out." );
116
124
}
117
125
return null ;
118
126
}
119
127
128
+ private Throwable getCause (Throwable t ) {
129
+ return t instanceof CompletionException && t .getCause () != null ? t .getCause () : t ;
130
+ }
131
+
120
132
private ExecutionResult toErrorResult (Throwable t ) {
121
133
String message =
122
- t instanceof CompletionException && t .getCause () != null
123
- ? t .getCause ().getMessage ()
134
+ t instanceof CancellationException
135
+ ? "Execution canceled because timeout of "
136
+ + configuration .getAsyncTimeout ()
137
+ + " millis was reached"
124
138
: t .getMessage ();
139
+ if (message == null ) {
140
+ message = "Unexpected error occurred" ;
141
+ }
125
142
return new ExecutionResultImpl (new GenericGraphQLError (message ));
126
143
}
127
144
@@ -130,28 +147,28 @@ protected QueryResponseWriter createWriter(
130
147
return queryResponseWriterFactory .createWriter (invocationInput , queryResult , configuration );
131
148
}
132
149
133
- private CompletableFuture < GraphQLQueryResult > invoke (
150
+ private FutureExecutionResult invoke (
134
151
GraphQLInvocationInput invocationInput ,
135
152
HttpServletRequest request ,
136
153
HttpServletResponse response ) {
137
154
if (invocationInput instanceof GraphQLSingleInvocationInput ) {
138
- return graphQLInvoker .queryAsync (invocationInput );
155
+ return graphQLInvoker .execute (invocationInput );
139
156
}
140
157
return invokeBatched ((GraphQLBatchedInvocationInput ) invocationInput , request , response );
141
158
}
142
159
143
- private CompletableFuture < GraphQLQueryResult > invokeBatched (
160
+ private FutureExecutionResult invokeBatched (
144
161
GraphQLBatchedInvocationInput batchedInvocationInput ,
145
162
HttpServletRequest request ,
146
163
HttpServletResponse response ) {
147
164
BatchInputPreProcessor preprocessor = configuration .getBatchInputPreProcessor ();
148
165
BatchInputPreProcessResult result =
149
166
preprocessor .preProcessBatch (batchedInvocationInput , request , response );
150
167
if (result .isExecutable ()) {
151
- return graphQLInvoker .queryAsync (result .getBatchedInvocationInput ());
168
+ return graphQLInvoker .execute (result .getBatchedInvocationInput ());
152
169
}
153
170
154
- return CompletableFuture . completedFuture (
171
+ return FutureExecutionResult . error (
155
172
GraphQLQueryResult .createError (result .getStatusCode (), result .getStatusMessage ()));
156
173
}
157
174
}
0 commit comments