25
25
26
26
import org .junit .jupiter .api .AfterEach ;
27
27
import org .junit .jupiter .api .Test ;
28
+ import org .junit .jupiter .api .Timeout ;
28
29
import org .junit .jupiter .params .ParameterizedTest ;
29
30
import org .junit .jupiter .params .provider .ValueSource ;
30
31
33
34
import org .springframework .context .annotation .Bean ;
34
35
import org .springframework .context .annotation .Configuration ;
35
36
import org .springframework .context .support .PropertySourcesPlaceholderConfigurer ;
37
+ import org .springframework .core .task .TaskExecutor ;
36
38
import org .springframework .core .testfixture .EnabledForTestGroups ;
37
39
import org .springframework .scheduling .TaskScheduler ;
38
40
import org .springframework .scheduling .concurrent .SimpleAsyncTaskScheduler ;
@@ -66,6 +68,10 @@ public void tearDown() {
66
68
}
67
69
68
70
71
+ /*
72
+ * Tests compatibility between default executor in TaskSchedulerRouter
73
+ * and explicit ThreadPoolTaskScheduler in configuration subclass.
74
+ */
69
75
@ ParameterizedTest
70
76
@ ValueSource (classes = {FixedRateTaskConfig .class , FixedRateTaskConfigSubclass .class })
71
77
@ EnabledForTestGroups (LONG_RUNNING )
@@ -77,8 +83,14 @@ public void withFixedRateTask(Class<?> configClass) throws InterruptedException
77
83
assertThat (ctx .getBean (AtomicInteger .class ).get ()).isGreaterThanOrEqualTo (10 );
78
84
}
79
85
86
+ /*
87
+ * Tests compatibility between SimpleAsyncTaskScheduler in regular configuration
88
+ * and explicit ThreadPoolTaskScheduler in configuration subclass. This includes
89
+ * pause/resume behavior and a controlled shutdown with a 1s termination timeout.
90
+ */
80
91
@ ParameterizedTest
81
92
@ ValueSource (classes = {ExplicitSchedulerConfig .class , ExplicitSchedulerConfigSubclass .class })
93
+ @ Timeout (2 ) // should actually complete within 1s
82
94
@ EnabledForTestGroups (LONG_RUNNING )
83
95
public void withExplicitScheduler (Class <?> configClass ) throws InterruptedException {
84
96
ctx = new AnnotationConfigApplicationContext (configClass );
@@ -96,9 +108,35 @@ public void withExplicitScheduler(Class<?> configClass) throws InterruptedExcept
96
108
int count3 = ctx .getBean (AtomicInteger .class ).get ();
97
109
assertThat (count3 ).isGreaterThanOrEqualTo (20 );
98
110
111
+ TaskExecutor executor = ctx .getBean (TaskExecutor .class );
112
+ AtomicInteger count = new AtomicInteger (0 );
113
+ for (int i = 0 ; i < 2 ; i ++) {
114
+ executor .execute (() -> {
115
+ try {
116
+ Thread .sleep (10000 ); // try to break test timeout
117
+ }
118
+ catch (InterruptedException ex ) {
119
+ // expected during executor shutdown
120
+ try {
121
+ Thread .sleep (500 );
122
+ // should get here within task termination timeout (1000)
123
+ count .incrementAndGet ();
124
+ }
125
+ catch (InterruptedException ex2 ) {
126
+ // not expected
127
+ }
128
+ }
129
+ });
130
+ }
131
+
99
132
assertThat (ctx .getBean (ExplicitSchedulerConfig .class ).threadName ).startsWith ("explicitScheduler-" );
100
- assertThat (Arrays .asList (ctx .getDefaultListableBeanFactory ().getDependentBeans ("myTaskScheduler" )).contains (
101
- TaskManagementConfigUtils .SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME )).isTrue ();
133
+ assertThat (Arrays .asList (ctx .getDefaultListableBeanFactory ().getDependentBeans ("myTaskScheduler" ))
134
+ .contains (TaskManagementConfigUtils .SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME )).isTrue ();
135
+
136
+ // Include executor shutdown in test timeout (2 seconds),
137
+ // expecting interruption of the sleeping thread...
138
+ ctx .close ();
139
+ assertThat (count .intValue ()).isEqualTo (2 );
102
140
}
103
141
104
142
@ Test
@@ -226,6 +264,11 @@ public void task() {
226
264
227
265
@ Configuration
228
266
static class FixedRateTaskConfigSubclass extends FixedRateTaskConfig {
267
+
268
+ @ Bean
269
+ public TaskScheduler taskScheduler () {
270
+ return new ThreadPoolTaskScheduler ();
271
+ }
229
272
}
230
273
231
274
@@ -239,6 +282,7 @@ static class ExplicitSchedulerConfig {
239
282
public TaskScheduler myTaskScheduler () {
240
283
SimpleAsyncTaskScheduler scheduler = new SimpleAsyncTaskScheduler ();
241
284
scheduler .setThreadNamePrefix ("explicitScheduler-" );
285
+ scheduler .setTaskTerminationTimeout (1000 );
242
286
return scheduler ;
243
287
}
244
288
@@ -263,6 +307,8 @@ static class ExplicitSchedulerConfigSubclass extends ExplicitSchedulerConfig {
263
307
public TaskScheduler myTaskScheduler () {
264
308
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler ();
265
309
scheduler .setThreadNamePrefix ("explicitScheduler-" );
310
+ scheduler .setAwaitTerminationMillis (1000 );
311
+ scheduler .setPoolSize (2 );
266
312
return scheduler ;
267
313
}
268
314
}
@@ -437,6 +483,7 @@ public void task() {
437
483
public TaskScheduler taskScheduler1 () {
438
484
SimpleAsyncTaskScheduler scheduler = new SimpleAsyncTaskScheduler ();
439
485
scheduler .setThreadNamePrefix ("explicitScheduler1" );
486
+ scheduler .setConcurrencyLimit (1 );
440
487
return scheduler ;
441
488
}
442
489
@@ -478,6 +525,7 @@ public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
478
525
public TaskScheduler taskScheduler1 () {
479
526
SimpleAsyncTaskScheduler scheduler = new SimpleAsyncTaskScheduler ();
480
527
scheduler .setThreadNamePrefix ("explicitScheduler1-" );
528
+ scheduler .setConcurrencyLimit (1 );
481
529
return scheduler ;
482
530
}
483
531
@@ -508,6 +556,7 @@ public ThreadAwareWorker worker() {
508
556
public TaskScheduler taskScheduler1 () {
509
557
SimpleAsyncTaskScheduler scheduler = new SimpleAsyncTaskScheduler ();
510
558
scheduler .setThreadNamePrefix ("explicitScheduler1-" );
559
+ scheduler .setConcurrencyLimit (1 );
511
560
return scheduler ;
512
561
}
513
562
0 commit comments