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 #cancelInternal} 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 () {
@@ -192,7 +228,8 @@ public void start(ClientStreamListener listener) {
192
228
boolean savedPassThrough ;
193
229
synchronized (this ) {
194
230
this .listener = checkNotNull (listener , "listener" );
195
- // If error != null, then cancel() has been called and was unable to close the listener
231
+ // If error != null, then cancelInternal() has been called and was unable to close the
232
+ // listener
196
233
savedError = error ;
197
234
savedPassThrough = passThrough ;
198
235
if (!savedPassThrough ) {
@@ -218,7 +255,7 @@ public void run() {
218
255
}
219
256
220
257
@ Override
221
- public void writeMessage (final InputStream message ) {
258
+ public final void writeMessage (final InputStream message ) {
222
259
checkNotNull (message , "message" );
223
260
if (passThrough ) {
224
261
realStream .writeMessage (message );
@@ -233,7 +270,7 @@ public void run() {
233
270
}
234
271
235
272
@ Override
236
- public void flush () {
273
+ public final void flush () {
237
274
if (passThrough ) {
238
275
realStream .flush ();
239
276
} else {
@@ -253,12 +290,12 @@ public void cancel(final Status reason) {
253
290
boolean delegateToRealStream = true ;
254
291
ClientStreamListener listenerToClose = null ;
255
292
synchronized (this ) {
256
- // If realStream != null, then either setStream() or cancel() has been called
293
+ if (listener == null ) {
294
+ throw new IllegalStateException ("cancel() must be called after start()" );
295
+ }
257
296
if (realStream == null ) {
258
297
realStream = NoopClientStream .INSTANCE ;
259
298
delegateToRealStream = false ;
260
-
261
- // If listener == null, then start() will later call listener with 'error'
262
299
listenerToClose = listener ;
263
300
error = reason ;
264
301
}
@@ -271,15 +308,13 @@ public void run() {
271
308
}
272
309
});
273
310
} else {
274
- if (listenerToClose != null ) {
275
- listenerToClose .closed (reason , new Metadata ());
276
- }
311
+ listenerToClose .closed (reason , new Metadata ());
277
312
drainPendingCalls ();
278
313
}
279
314
}
280
315
281
316
@ Override
282
- public void halfClose () {
317
+ public final void halfClose () {
283
318
delayOrExecute (new Runnable () {
284
319
@ Override
285
320
public void run () {
@@ -289,7 +324,7 @@ public void run() {
289
324
}
290
325
291
326
@ Override
292
- public void request (final int numMessages ) {
327
+ public final void request (final int numMessages ) {
293
328
if (passThrough ) {
294
329
realStream .request (numMessages );
295
330
} else {
@@ -303,7 +338,7 @@ public void run() {
303
338
}
304
339
305
340
@ Override
306
- public void setCompressor (final Compressor compressor ) {
341
+ public final void setCompressor (final Compressor compressor ) {
307
342
checkNotNull (compressor , "compressor" );
308
343
delayOrExecute (new Runnable () {
309
344
@ Override
@@ -314,7 +349,7 @@ public void run() {
314
349
}
315
350
316
351
@ Override
317
- public void setDecompressor (Decompressor decompressor ) {
352
+ public final void setDecompressor (Decompressor decompressor ) {
318
353
checkNotNull (decompressor , "decompressor" );
319
354
// This method being called only makes sense after setStream() has been called (but not
320
355
// necessarily returned), but there is not necessarily a happens-before relationship. This
@@ -327,7 +362,7 @@ public void setDecompressor(Decompressor decompressor) {
327
362
}
328
363
329
364
@ Override
330
- public boolean isReady () {
365
+ public final boolean isReady () {
331
366
if (passThrough ) {
332
367
return realStream .isReady ();
333
368
} else {
@@ -336,7 +371,7 @@ public boolean isReady() {
336
371
}
337
372
338
373
@ Override
339
- public void setMessageCompression (final boolean enable ) {
374
+ public final void setMessageCompression (final boolean enable ) {
340
375
if (passThrough ) {
341
376
realStream .setMessageCompression (enable );
342
377
} else {
0 commit comments