18
18
19
19
import java .lang .reflect .InvocationTargetException ;
20
20
import java .lang .reflect .Method ;
21
+ import java .util .List ;
21
22
import java .util .concurrent .CountDownLatch ;
22
23
23
24
import org .apache .commons .logging .Log ;
32
33
import org .springframework .core .KotlinDetector ;
33
34
import org .springframework .core .ReactiveAdapter ;
34
35
import org .springframework .core .ReactiveAdapterRegistry ;
36
+ import org .springframework .lang .Nullable ;
35
37
import org .springframework .util .Assert ;
36
38
import org .springframework .util .ClassUtils ;
37
39
import org .springframework .util .ReflectionUtils ;
40
+ import org .springframework .util .StringUtils ;
38
41
39
42
/**
40
43
* Helper class for @{@link ScheduledAnnotationBeanPostProcessor} to support reactive cases
@@ -148,33 +151,16 @@ static Publisher<?> getPublisherFor(Method method, Object bean) {
148
151
* (i.e. the task blocks until completion of the Publisher, then the delay is applied
149
152
* until next iteration).
150
153
*/
151
- static Runnable createSubscriptionRunnable (Method method , Object targetBean , boolean isFixedDelaySpecialCase ) {
154
+ static Runnable createSubscriptionRunnable (Method method , Object targetBean , Scheduled scheduled ,
155
+ List <Runnable > subscriptionTrackerRegistry ) {
156
+ boolean shouldBlock = scheduled .fixedDelay () > 0 || StringUtils .hasText (scheduled .fixedDelayString ());
152
157
final Publisher <?> publisher = getPublisherFor (method , targetBean );
153
- if (isFixedDelaySpecialCase ) {
158
+ if (shouldBlock ) {
154
159
return () -> {
155
160
final CountDownLatch latch = new CountDownLatch (1 );
156
- publisher .subscribe (new Subscriber <Object >() {
157
- @ Override
158
- public void onSubscribe (Subscription s ) {
159
- s .request (Integer .MAX_VALUE );
160
- }
161
-
162
- @ Override
163
- public void onNext (Object o ) {
164
- // NO-OP
165
- }
166
-
167
- @ Override
168
- public void onError (Throwable ex ) {
169
- LOGGER .warn ("Unexpected error occurred in scheduled reactive task" , ex );
170
- latch .countDown ();
171
- }
172
-
173
- @ Override
174
- public void onComplete () {
175
- latch .countDown ();
176
- }
177
- });
161
+ TrackingSubscriber subscriber = new TrackingSubscriber (subscriptionTrackerRegistry , latch );
162
+ subscriptionTrackerRegistry .add (subscriber );
163
+ publisher .subscribe (subscriber );
178
164
try {
179
165
latch .await ();
180
166
}
@@ -183,27 +169,80 @@ public void onComplete() {
183
169
}
184
170
};
185
171
}
186
- return () -> publisher .subscribe (new Subscriber <Object >() {
187
- @ Override
188
- public void onSubscribe (Subscription s ) {
189
- s .request (Integer .MAX_VALUE );
190
- }
172
+ return () -> {
173
+ final TrackingSubscriber subscriber = new TrackingSubscriber (subscriptionTrackerRegistry );
174
+ subscriptionTrackerRegistry .add (subscriber );
175
+ publisher .subscribe (subscriber );
176
+ };
177
+ }
178
+
179
+ /**
180
+ * A {@code Subscriber} which keeps track of its {@code Subscription} and exposes the
181
+ * capacity to cancel the subscription as a {@code Runnable}. Can optionally support
182
+ * blocking if a {@code CountDownLatch} is passed at construction.
183
+ */
184
+ private static final class TrackingSubscriber implements Subscriber <Object >, Runnable {
185
+
186
+ private final List <Runnable > subscriptionTrackerRegistry ;
187
+
188
+ @ Nullable
189
+ private final CountDownLatch blockingLatch ;
190
+
191
+ /*
192
+ Implementation note: since this is created last minute when subscribing,
193
+ there shouldn't be a way to cancel the tracker externally from the
194
+ ScheduledAnnotationBeanProcessor before the #setSubscription(Subscription)
195
+ method is called.
196
+ */
197
+ @ Nullable
198
+ private Subscription s ;
199
+
200
+ TrackingSubscriber (List <Runnable > subscriptionTrackerRegistry ) {
201
+ this (subscriptionTrackerRegistry , null );
202
+ }
191
203
192
- @ Override
193
- public void onNext (Object o ) {
194
- // NO-OP
204
+ TrackingSubscriber (List <Runnable > subscriptionTrackerRegistry , @ Nullable CountDownLatch latch ) {
205
+ this .subscriptionTrackerRegistry = subscriptionTrackerRegistry ;
206
+ this .blockingLatch = latch ;
207
+ }
208
+
209
+ @ Override
210
+ public void run () {
211
+ if (this .s != null ) {
212
+ this .s .cancel ();
213
+ }
214
+ if (this .blockingLatch != null ) {
215
+ this .blockingLatch .countDown ();
195
216
}
217
+ }
218
+
219
+ @ Override
220
+ public void onSubscribe (Subscription s ) {
221
+ this .s = s ;
222
+ s .request (Integer .MAX_VALUE );
223
+ }
224
+
225
+ @ Override
226
+ public void onNext (Object o ) {
227
+ // NO-OP
228
+ }
196
229
197
- @ Override
198
- public void onError (Throwable ex ) {
199
- LOGGER .warn ("Unexpected error occurred in scheduled reactive task" , ex );
230
+ @ Override
231
+ public void onError (Throwable ex ) {
232
+ this .subscriptionTrackerRegistry .remove (this );
233
+ LOGGER .warn ("Unexpected error occurred in scheduled reactive task" , ex );
234
+ if (this .blockingLatch != null ) {
235
+ this .blockingLatch .countDown ();
200
236
}
237
+ }
201
238
202
- @ Override
203
- public void onComplete () {
204
- // NO-OP
239
+ @ Override
240
+ public void onComplete () {
241
+ this .subscriptionTrackerRegistry .remove (this );
242
+ if (this .blockingLatch != null ) {
243
+ this .blockingLatch .countDown ();
205
244
}
206
- });
245
+ }
207
246
}
208
247
209
248
}
0 commit comments