15
15
16
16
package software .amazon .awssdk .transfer .s3 .internal ;
17
17
18
- import java .util .Queue ;
18
+ import java .util .Optional ;
19
19
import java .util .concurrent .CompletableFuture ;
20
- import java .util .concurrent .ConcurrentLinkedQueue ;
21
20
import java .util .concurrent .atomic .AtomicBoolean ;
22
21
import java .util .concurrent .atomic .AtomicInteger ;
23
22
import java .util .function .Function ;
26
25
import software .amazon .awssdk .annotations .SdkInternalApi ;
27
26
import software .amazon .awssdk .utils .Logger ;
28
27
import software .amazon .awssdk .utils .Validate ;
28
+ import software .amazon .awssdk .utils .async .DemandIgnoringSubscription ;
29
+ import software .amazon .awssdk .utils .async .StoringSubscriber ;
29
30
30
31
/**
31
32
* An implementation of {@link Subscriber} that execute the provided function for every event and limits the number of concurrent
36
37
@ SdkInternalApi
37
38
public class AsyncBufferingSubscriber <T > implements Subscriber <T > {
38
39
private static final Logger log = Logger .loggerFor (AsyncBufferingSubscriber .class );
39
- private static final Object COMPLETE_EVENT = new Object ();
40
- private final Queue <Object > buffer ;
41
40
private final CompletableFuture <?> returnFuture ;
42
41
private final Function <T , CompletableFuture <?>> consumer ;
43
42
private final int maxConcurrentExecutions ;
44
43
private final AtomicInteger numRequestsInFlight ;
45
44
private final AtomicBoolean isDelivering = new AtomicBoolean (false );
46
45
private volatile boolean isStreamingDone ;
47
- private volatile Subscription subscription ;
46
+ private Subscription subscription ;
47
+
48
+ private final StoringSubscriber <T > storingSubscriber ;
48
49
49
50
public AsyncBufferingSubscriber (Function <T , CompletableFuture <?>> consumer ,
50
51
CompletableFuture <Void > returnFuture ,
51
52
int maxConcurrentExecutions ) {
52
- this .buffer = new ConcurrentLinkedQueue <>();
53
53
this .returnFuture = returnFuture ;
54
54
this .consumer = consumer ;
55
55
this .maxConcurrentExecutions = maxConcurrentExecutions ;
56
56
this .numRequestsInFlight = new AtomicInteger (0 );
57
+ this .storingSubscriber = new StoringSubscriber <>(Integer .MAX_VALUE );
57
58
}
58
59
59
60
@ Override
@@ -64,68 +65,53 @@ public void onSubscribe(Subscription subscription) {
64
65
subscription .cancel ();
65
66
return ;
66
67
}
68
+ storingSubscriber .onSubscribe (new DemandIgnoringSubscription (subscription ));
67
69
this .subscription = subscription ;
68
70
subscription .request (maxConcurrentExecutions );
69
71
}
70
72
71
73
@ Override
72
74
public void onNext (T item ) {
73
- if (item == null ) {
74
- subscription .cancel ();
75
- NullPointerException exception = new NullPointerException ("Item must not be null" );
76
- returnFuture .completeExceptionally (exception );
77
- throw exception ;
78
- }
79
-
80
- try {
81
- buffer .add (item );
82
- flushBufferIfNeeded ();
83
- } catch (Exception e ) {
84
- isStreamingDone = true ;
85
- subscription .cancel ();
86
- returnFuture .completeExceptionally (e );
87
- }
75
+ storingSubscriber .onNext (item );
76
+ flushBufferIfNeeded ();
88
77
}
89
78
90
79
private void flushBufferIfNeeded () {
91
- if (buffer .isEmpty ()) {
92
- if (isStreamingDone && numRequestsInFlight .get () == 0 ) {
93
- returnFuture .complete (null );
94
- } else {
95
- subscription .request (1 );
96
- }
97
- return ;
98
- }
99
-
100
80
if (isDelivering .compareAndSet (false , true )) {
101
81
try {
102
- Object firstEvent = buffer .peek ();
103
- if (isCompleteEvent (firstEvent )) {
104
- Object event = buffer .poll ();
105
- handleCompleteEvent (event );
106
- return ;
107
- }
108
-
109
- while (!buffer .isEmpty () && numRequestsInFlight .get () < maxConcurrentExecutions ) {
110
- Object item = buffer .poll ();
111
- if (item == null ) {
82
+ Optional <StoringSubscriber .Event <T >> next = storingSubscriber .peek ();
83
+ while (numRequestsInFlight .get () < maxConcurrentExecutions ) {
84
+ if (!next .isPresent ()) {
85
+ subscription .request (1 );
112
86
break ;
113
87
}
114
88
115
- if (isCompleteEvent (item )) {
116
- handleCompleteEvent (item );
117
- return ;
89
+ switch (next .get ().type ()) {
90
+ case ON_COMPLETE :
91
+ handleCompleteEvent ();
92
+ break ;
93
+ case ON_ERROR :
94
+ handleError (next .get ().runtimeError ());
95
+ break ;
96
+ case ON_NEXT :
97
+ handleOnNext (next .get ().value ());
98
+ break ;
99
+ default :
100
+ handleError (new IllegalStateException ("Unknown stored type: " + next .get ().type ()));
101
+ break ;
118
102
}
119
103
120
- deliverItem (( T ) item );
104
+ next = storingSubscriber . peek ( );
121
105
}
122
106
} finally {
123
107
isDelivering .set (false );
124
108
}
125
109
}
126
110
}
127
111
128
- private void deliverItem (T item ) {
112
+ private void handleOnNext (T item ) {
113
+ storingSubscriber .poll ();
114
+
129
115
int numberOfRequestInFlight = numRequestsInFlight .incrementAndGet ();
130
116
log .debug (() -> "Delivering next item, numRequestInFlight=" + numberOfRequestInFlight );
131
117
@@ -139,26 +125,28 @@ private void deliverItem(T item) {
139
125
});
140
126
}
141
127
142
- private void handleCompleteEvent (Object event ) {
143
- isStreamingDone = true ;
128
+ private void handleCompleteEvent () {
144
129
if (numRequestsInFlight .get () == 0 ) {
145
130
returnFuture .complete (null );
131
+ storingSubscriber .poll ();
146
132
}
147
133
}
148
134
149
135
@ Override
150
136
public void onError (Throwable t ) {
151
137
handleError (t );
138
+ storingSubscriber .onError (t );
152
139
}
153
140
154
141
private void handleError (Throwable t ) {
155
142
returnFuture .completeExceptionally (t );
156
- buffer . clear ();
143
+ storingSubscriber . poll ();
157
144
}
158
145
159
146
@ Override
160
147
public void onComplete () {
161
- buffer .add (COMPLETE_EVENT );
148
+ isStreamingDone = true ;
149
+ storingSubscriber .onComplete ();
162
150
flushBufferIfNeeded ();
163
151
}
164
152
@@ -168,8 +156,4 @@ public void onComplete() {
168
156
public int numRequestsInFlight () {
169
157
return numRequestsInFlight .get ();
170
158
}
171
-
172
- private static boolean isCompleteEvent (Object event ) {
173
- return COMPLETE_EVENT .equals (event );
174
- }
175
159
}
0 commit comments