21
21
import com .mongodb .ServerCursor ;
22
22
import com .mongodb .client .MongoCursor ;
23
23
import com .mongodb .lang .Nullable ;
24
+ import com .mongodb .reactivestreams .client .internal .BatchCursor ;
24
25
import org .reactivestreams .Publisher ;
25
26
import org .reactivestreams .Subscriber ;
26
27
import org .reactivestreams .Subscription ;
28
+ import reactor .core .CoreSubscriber ;
27
29
import reactor .core .publisher .Flux ;
30
+ import reactor .core .publisher .Hooks ;
31
+ import reactor .core .publisher .Operators ;
32
+ import reactor .util .context .Context ;
28
33
29
34
import java .util .NoSuchElementException ;
30
35
import java .util .concurrent .BlockingDeque ;
36
+ import java .util .concurrent .CompletableFuture ;
31
37
import java .util .concurrent .CountDownLatch ;
38
+ import java .util .concurrent .ExecutionException ;
32
39
import java .util .concurrent .LinkedBlockingDeque ;
33
40
import java .util .concurrent .TimeUnit ;
41
+ import java .util .concurrent .TimeoutException ;
34
42
35
43
import static com .mongodb .ClusterFixture .TIMEOUT ;
36
44
import static com .mongodb .internal .thread .InterruptionUtil .interruptAndCreateMongoInterruptedException ;
41
49
class SyncMongoCursor <T > implements MongoCursor <T > {
42
50
private static final Object COMPLETED = new Object ();
43
51
private final BlockingDeque <Object > results = new LinkedBlockingDeque <>();
52
+ private final CompletableFuture <BatchCursor > batchCursorCompletableFuture = new CompletableFuture <>();
44
53
private final Integer batchSize ;
45
54
private int countToBatchSize ;
46
55
private Subscription subscription ;
@@ -50,7 +59,11 @@ class SyncMongoCursor<T> implements MongoCursor<T> {
50
59
51
60
SyncMongoCursor (final Publisher <T > publisher , @ Nullable final Integer batchSize ) {
52
61
this .batchSize = batchSize ;
53
- CountDownLatch latch = new CountDownLatch (1 );
62
+ CountDownLatch subscriptionLatch = new CountDownLatch (1 );
63
+ Hooks .onEachOperator (Operators .lift ((sc , sub ) ->
64
+ new BatchCursorInterceptSubscriber (sub , batchCursorCompletableFuture
65
+ )));
66
+
54
67
//noinspection ReactiveStreamsSubscriberImplementation
55
68
Flux .from (publisher ).contextWrite (CONTEXT ).subscribe (new Subscriber <T >() {
56
69
@ Override
@@ -61,7 +74,7 @@ public void onSubscribe(final Subscription s) {
61
74
} else {
62
75
subscription .request (batchSize );
63
76
}
64
- latch .countDown ();
77
+ subscriptionLatch .countDown ();
65
78
}
66
79
67
80
@ Override
@@ -80,12 +93,20 @@ public void onComplete() {
80
93
}
81
94
});
82
95
try {
83
- if (!latch .await (TIMEOUT , TimeUnit .SECONDS )) {
96
+ if (!subscriptionLatch .await (TIMEOUT , TimeUnit .SECONDS )) {
84
97
throw new MongoTimeoutException ("Timeout waiting for subscription" );
85
98
}
99
+ batchCursorCompletableFuture .get (TIMEOUT , TimeUnit .SECONDS );
100
+ Hooks .resetOnEachOperator ();
86
101
sleep (getSleepAfterCursorOpen ());
87
102
} catch (InterruptedException e ) {
88
103
throw interruptAndCreateMongoInterruptedException ("Interrupted waiting for asynchronous cursor establishment" , e );
104
+ } catch (ExecutionException | TimeoutException e ) {
105
+ Throwable cause = e .getCause ();
106
+ if (cause instanceof RuntimeException ) {
107
+ throw (RuntimeException ) cause ;
108
+ }
109
+ throw new RuntimeException (e );
89
110
}
90
111
}
91
112
@@ -181,4 +202,50 @@ private RuntimeException translateError(final Throwable throwable) {
181
202
}
182
203
return new RuntimeException (throwable );
183
204
}
205
+
206
+
207
+ static class BatchCursorInterceptSubscriber implements CoreSubscriber {
208
+
209
+ private final CoreSubscriber sub ;
210
+ private final CompletableFuture <BatchCursor > batchCursorCompletableFuture ;
211
+
212
+
213
+ BatchCursorInterceptSubscriber (final CoreSubscriber sub ,
214
+ final CompletableFuture <BatchCursor > batchCursorCompletableFuture ) {
215
+ this .sub = sub ;
216
+ this .batchCursorCompletableFuture = batchCursorCompletableFuture ;
217
+ }
218
+
219
+ @ Override
220
+ public Context currentContext () {
221
+ return sub .currentContext ();
222
+ }
223
+
224
+ @ Override
225
+ public void onSubscribe (final Subscription s ) {
226
+ sub .onSubscribe (s );
227
+ }
228
+
229
+ @ Override
230
+ public void onNext (final Object o ) {
231
+ if (o instanceof BatchCursor ) {
232
+ // Interception of a cursor means that it has been created at this point.
233
+ batchCursorCompletableFuture .complete ((BatchCursor ) o );
234
+ }
235
+ sub .onNext (o );
236
+ }
237
+
238
+ @ Override
239
+ public void onError (final Throwable t ) {
240
+ if (!batchCursorCompletableFuture .isDone ()) { // Cursor has not been created yet but an error occurred.
241
+ batchCursorCompletableFuture .completeExceptionally (t );
242
+ }
243
+ sub .onError (t );
244
+ }
245
+
246
+ @ Override
247
+ public void onComplete () {
248
+ sub .onComplete ();
249
+ }
250
+ }
184
251
}
0 commit comments