34
34
import com .google .api .core .BetaApi ;
35
35
import com .google .api .gax .core .BackgroundResource ;
36
36
import com .google .api .gax .core .InstantiatingExecutorProvider ;
37
+ import com .google .common .annotations .VisibleForTesting ;
37
38
import com .google .common .base .Preconditions ;
38
39
import java .io .IOException ;
39
40
import java .util .concurrent .Executor ;
47
48
@ BetaApi
48
49
public class ManagedHttpJsonChannel implements HttpJsonChannel , BackgroundResource {
49
50
50
- private static final ExecutorService DEFAULT_EXECUTOR =
51
- InstantiatingExecutorProvider .newBuilder ().build ().getExecutor ();
52
-
53
51
private final Executor executor ;
52
+ private final boolean usingDefaultExecutor ;
54
53
private final String endpoint ;
55
54
private final HttpTransport httpTransport ;
56
55
private final ScheduledExecutorService deadlineScheduledExecutorService ;
57
-
58
56
private boolean isTransportShutdown ;
59
57
60
58
protected ManagedHttpJsonChannel () {
61
- this (null , null , null );
59
+ this (null , true , null , null );
62
60
}
63
61
64
62
private ManagedHttpJsonChannel (
65
- Executor executor , String endpoint , @ Nullable HttpTransport httpTransport ) {
63
+ Executor executor ,
64
+ boolean usingDefaultExecutor ,
65
+ String endpoint ,
66
+ @ Nullable HttpTransport httpTransport ) {
66
67
this .executor = executor ;
68
+ this .usingDefaultExecutor = usingDefaultExecutor ;
67
69
this .endpoint = endpoint ;
68
70
this .httpTransport = httpTransport == null ? new NetHttpTransport () : httpTransport ;
69
71
this .deadlineScheduledExecutorService = Executors .newSingleThreadScheduledExecutor ();
@@ -82,58 +84,120 @@ public <RequestT, ResponseT> HttpJsonClientCall<RequestT, ResponseT> newCall(
82
84
deadlineScheduledExecutorService );
83
85
}
84
86
87
+ @ VisibleForTesting
88
+ Executor getExecutor () {
89
+ return executor ;
90
+ }
91
+
85
92
@ Override
86
93
public synchronized void shutdown () {
94
+ // Calling shutdown/ shutdownNow() twice should no-op
87
95
if (isTransportShutdown ) {
88
96
return ;
89
97
}
90
98
try {
99
+ // Only shutdown the executor if it was created by Gax. External executors
100
+ // should be managed by the user.
101
+ if (shouldManageExecutor ()) {
102
+ ((ExecutorService ) executor ).shutdown ();
103
+ }
91
104
deadlineScheduledExecutorService .shutdown ();
92
105
httpTransport .shutdown ();
93
106
isTransportShutdown = true ;
94
107
} catch (IOException e ) {
95
- e .printStackTrace ();
108
+ // TODO: Log this scenario once we implemented the Cloud SDK logging.
109
+ // Swallow error if httpTransport shutdown fails
96
110
}
97
111
}
98
112
99
113
@ Override
100
114
public boolean isShutdown () {
101
- return isTransportShutdown ;
115
+ // TODO(lawrenceqiu): Expose an isShutdown() method for HttpTransport
116
+ boolean isShutdown = isTransportShutdown && deadlineScheduledExecutorService .isShutdown ();
117
+ // Check that the Gax's ExecutorService is shutdown as well
118
+ if (shouldManageExecutor ()) {
119
+ isShutdown = isShutdown && ((ExecutorService ) executor ).isShutdown ();
120
+ }
121
+ return isShutdown ;
102
122
}
103
123
104
124
@ Override
105
125
public boolean isTerminated () {
106
- return isTransportShutdown ;
126
+ boolean isTerminated = deadlineScheduledExecutorService .isTerminated ();
127
+ // Check that the Gax's ExecutorService is terminated as well
128
+ if (shouldManageExecutor ()) {
129
+ isTerminated = isTerminated && ((ExecutorService ) executor ).isTerminated ();
130
+ }
131
+ return isTerminated ;
107
132
}
108
133
109
134
@ Override
110
135
public void shutdownNow () {
111
- shutdown ();
136
+ // Calling shutdown/ shutdownNow() twice should no-op
137
+ if (isTransportShutdown ) {
138
+ return ;
139
+ }
140
+ try {
141
+ // Only shutdown the executor if it was created by Gax. External executors
142
+ // should be managed by the user.
143
+ if (shouldManageExecutor ()) {
144
+ ((ExecutorService ) executor ).shutdownNow ();
145
+ }
146
+ deadlineScheduledExecutorService .shutdownNow ();
147
+ httpTransport .shutdown ();
148
+ isTransportShutdown = true ;
149
+ } catch (IOException e ) {
150
+ // TODO: Log this scenario once we implemented the Cloud SDK logging.
151
+ // Swallow error if httpTransport shutdown fails
152
+ }
112
153
}
113
154
114
155
@ Override
115
156
public boolean awaitTermination (long duration , TimeUnit unit ) throws InterruptedException {
116
- // TODO
117
- return false ;
157
+ long endTimeNanos = System .nanoTime () + unit .toNanos (duration );
158
+ long awaitTimeNanos = endTimeNanos - System .nanoTime ();
159
+ if (awaitTimeNanos <= 0 ) {
160
+ return false ;
161
+ }
162
+ // Only awaitTermination for the executor if it was created by Gax. External executors
163
+ // should be managed by the user.
164
+ if (usingDefaultExecutor && executor instanceof ExecutorService ) {
165
+ boolean terminated = ((ExecutorService ) executor ).awaitTermination (awaitTimeNanos , unit );
166
+ // Termination duration has elapsed
167
+ if (!terminated ) {
168
+ return false ;
169
+ }
170
+ }
171
+ awaitTimeNanos = endTimeNanos - System .nanoTime ();
172
+ return deadlineScheduledExecutorService .awaitTermination (awaitTimeNanos , unit );
173
+ }
174
+
175
+ private boolean shouldManageExecutor () {
176
+ return usingDefaultExecutor && executor instanceof ExecutorService ;
118
177
}
119
178
120
179
@ Override
121
- public void close () {}
180
+ public void close () {
181
+ shutdown ();
182
+ }
122
183
123
184
public static Builder newBuilder () {
124
- return new Builder (). setExecutor ( DEFAULT_EXECUTOR ) ;
185
+ return new Builder ();
125
186
}
126
187
127
188
public static class Builder {
128
189
129
190
private Executor executor ;
130
191
private String endpoint ;
131
192
private HttpTransport httpTransport ;
193
+ private boolean usingDefaultExecutor ;
132
194
133
- private Builder () {}
195
+ private Builder () {
196
+ this .usingDefaultExecutor = false ;
197
+ }
134
198
135
199
public Builder setExecutor (Executor executor ) {
136
- this .executor = executor == null ? DEFAULT_EXECUTOR : executor ;
200
+ this .executor = executor ;
137
201
return this ;
138
202
}
139
203
@@ -150,8 +214,20 @@ public Builder setHttpTransport(HttpTransport httpTransport) {
150
214
public ManagedHttpJsonChannel build () {
151
215
Preconditions .checkNotNull (endpoint );
152
216
217
+ // If the executor provided for this channel is null, gax will provide a
218
+ // default executor to used for the calls. Only the default executor's
219
+ // lifecycle will be managed by the channel. Any external executor needs to
220
+ // managed by the user.
221
+ if (executor == null ) {
222
+ executor = InstantiatingExecutorProvider .newIOBuilder ().build ().getExecutor ();
223
+ usingDefaultExecutor = true ;
224
+ }
225
+
153
226
return new ManagedHttpJsonChannel (
154
- executor , endpoint , httpTransport == null ? new NetHttpTransport () : httpTransport );
227
+ executor ,
228
+ usingDefaultExecutor ,
229
+ endpoint ,
230
+ httpTransport == null ? new NetHttpTransport () : httpTransport );
155
231
}
156
232
}
157
233
}
0 commit comments