56
56
* necessary.
57
57
*/
58
58
class DelayedStream implements ClientStream {
59
+ @ VisibleForTesting
60
+ static final ClientStreamListener NOOP_STREAM_LISTENER = new ClientStreamListener () {
61
+ @ Override
62
+ public void messageRead (InputStream message ) {}
63
+
64
+ @ Override
65
+ public void onReady () {}
66
+
67
+ @ Override
68
+ public void headersRead (Metadata headers ) {}
69
+
70
+ @ Override
71
+ public void closed (Status status , Metadata trailers ) {}
72
+ };
73
+
59
74
/** {@code true} once realStream is valid and all pending calls have been drained. */
60
75
private volatile boolean passThrough ;
61
76
/**
@@ -73,7 +88,7 @@ class DelayedStream implements ClientStream {
73
88
private DelayedStreamListener delayedListener ;
74
89
75
90
@ Override
76
- public void setMaxInboundMessageSize (final int maxSize ) {
91
+ public final void setMaxInboundMessageSize (final int maxSize ) {
77
92
if (passThrough ) {
78
93
realStream .setMaxInboundMessageSize (maxSize );
79
94
} else {
@@ -87,7 +102,7 @@ public void run() {
87
102
}
88
103
89
104
@ Override
90
- public void setMaxOutboundMessageSize (final int maxSize ) {
105
+ public final void setMaxOutboundMessageSize (final int maxSize ) {
91
106
if (passThrough ) {
92
107
realStream .setMaxOutboundMessageSize (maxSize );
93
108
} else {
@@ -103,19 +118,40 @@ public void run() {
103
118
/**
104
119
* Transfers all pending and future requests and mutations to the given stream.
105
120
*
106
- * <p>No-op if either this method or {@link #cancel} have already been called.
121
+ * <p>This method must be called at most once. Extraneous calls will throw and end up cancelling
122
+ * the given streams.
123
+ *
124
+ * <p>If {@link #cancel} has been called, this method will cancel the given stream.
107
125
*/
108
126
// When this method returns, passThrough is guaranteed to be true
109
127
final void setStream (ClientStream stream ) {
128
+ ClientStream savedRealStream ;
129
+ Status savedError ;
110
130
synchronized (this ) {
111
- // If realStream != null, then either setStream() or cancel() has been called.
112
- if (realStream != null ) {
113
- return ;
131
+ savedRealStream = realStream ;
132
+ savedError = error ;
133
+ if (savedRealStream == null ) {
134
+ realStream = checkNotNull (stream , "stream" );
114
135
}
115
- realStream = checkNotNull (stream , "stream" );
116
136
}
117
137
118
- drainPendingCalls ();
138
+ if (savedRealStream == null ) {
139
+ drainPendingCalls ();
140
+ } else {
141
+ // If realStream was not null, then either setStream() or cancel() must had been called,
142
+ // we will cancel and discard the given stream.
143
+ // ClientStream.cancel() must be called after start()
144
+ stream .start (NOOP_STREAM_LISTENER );
145
+ if (savedError != null ) {
146
+ stream .cancel (savedError );
147
+ } else {
148
+ // If cancel() were called, error must have been non-null.
149
+ IllegalStateException exception = new IllegalStateException (
150
+ "DelayedStream.setStream() is called more than once" );
151
+ stream .cancel (Status .CANCELLED .withCause (exception ));
152
+ throw exception ;
153
+ }
154
+ }
119
155
}
120
156
121
157
/**
@@ -173,7 +209,7 @@ private void delayOrExecute(Runnable runnable) {
173
209
}
174
210
175
211
@ Override
176
- public void setAuthority (final String authority ) {
212
+ public final void setAuthority (final String authority ) {
177
213
checkState (listener == null , "May only be called before start" );
178
214
checkNotNull (authority , "authority" );
179
215
delayOrExecute (new Runnable () {
@@ -188,22 +224,15 @@ public void run() {
188
224
public void start (ClientStreamListener listener ) {
189
225
checkState (this .listener == null , "already started" );
190
226
191
- Status savedError ;
192
227
boolean savedPassThrough ;
193
228
synchronized (this ) {
194
229
this .listener = checkNotNull (listener , "listener" );
195
- // If error != null, then cancel() has been called and was unable to close the listener
196
- savedError = error ;
230
+ assert error == null ;
197
231
savedPassThrough = passThrough ;
198
232
if (!savedPassThrough ) {
199
233
listener = delayedListener = new DelayedStreamListener (listener );
200
234
}
201
235
}
202
- if (savedError != null ) {
203
- listener .closed (savedError , new Metadata ());
204
- return ;
205
- }
206
-
207
236
if (savedPassThrough ) {
208
237
realStream .start (listener );
209
238
} else {
@@ -218,7 +247,7 @@ public void run() {
218
247
}
219
248
220
249
@ Override
221
- public void writeMessage (final InputStream message ) {
250
+ public final void writeMessage (final InputStream message ) {
222
251
checkNotNull (message , "message" );
223
252
if (passThrough ) {
224
253
realStream .writeMessage (message );
@@ -233,7 +262,7 @@ public void run() {
233
262
}
234
263
235
264
@ Override
236
- public void flush () {
265
+ public final void flush () {
237
266
if (passThrough ) {
238
267
realStream .flush ();
239
268
} else {
@@ -253,12 +282,12 @@ public void cancel(final Status reason) {
253
282
boolean delegateToRealStream = true ;
254
283
ClientStreamListener listenerToClose = null ;
255
284
synchronized (this ) {
256
- // If realStream != null, then either setStream() or cancel() has been called
285
+ if (listener == null ) {
286
+ throw new IllegalStateException ("cancel() must be called after start()" );
287
+ }
257
288
if (realStream == null ) {
258
289
realStream = NoopClientStream .INSTANCE ;
259
290
delegateToRealStream = false ;
260
-
261
- // If listener == null, then start() will later call listener with 'error'
262
291
listenerToClose = listener ;
263
292
error = reason ;
264
293
}
@@ -271,15 +300,13 @@ public void run() {
271
300
}
272
301
});
273
302
} else {
274
- if (listenerToClose != null ) {
275
- listenerToClose .closed (reason , new Metadata ());
276
- }
303
+ listenerToClose .closed (reason , new Metadata ());
277
304
drainPendingCalls ();
278
305
}
279
306
}
280
307
281
308
@ Override
282
- public void halfClose () {
309
+ public final void halfClose () {
283
310
delayOrExecute (new Runnable () {
284
311
@ Override
285
312
public void run () {
@@ -289,7 +316,7 @@ public void run() {
289
316
}
290
317
291
318
@ Override
292
- public void request (final int numMessages ) {
319
+ public final void request (final int numMessages ) {
293
320
if (passThrough ) {
294
321
realStream .request (numMessages );
295
322
} else {
@@ -303,7 +330,7 @@ public void run() {
303
330
}
304
331
305
332
@ Override
306
- public void setCompressor (final Compressor compressor ) {
333
+ public final void setCompressor (final Compressor compressor ) {
307
334
checkNotNull (compressor , "compressor" );
308
335
delayOrExecute (new Runnable () {
309
336
@ Override
@@ -314,7 +341,7 @@ public void run() {
314
341
}
315
342
316
343
@ Override
317
- public void setDecompressor (Decompressor decompressor ) {
344
+ public final void setDecompressor (Decompressor decompressor ) {
318
345
checkNotNull (decompressor , "decompressor" );
319
346
// This method being called only makes sense after setStream() has been called (but not
320
347
// necessarily returned), but there is not necessarily a happens-before relationship. This
@@ -327,7 +354,7 @@ public void setDecompressor(Decompressor decompressor) {
327
354
}
328
355
329
356
@ Override
330
- public boolean isReady () {
357
+ public final boolean isReady () {
331
358
if (passThrough ) {
332
359
return realStream .isReady ();
333
360
} else {
@@ -336,7 +363,7 @@ public boolean isReady() {
336
363
}
337
364
338
365
@ Override
339
- public void setMessageCompression (final boolean enable ) {
366
+ public final void setMessageCompression (final boolean enable ) {
340
367
if (passThrough ) {
341
368
realStream .setMessageCompression (enable );
342
369
} else {
0 commit comments