Skip to content

Commit 7872d8a

Browse files
facewisemhalbritter
authored andcommitted
Make IntegrationTaskSchedulerConfiguration virtual threads aware
See gh-41695
1 parent 9e3e067 commit 7872d8a

File tree

2 files changed

+38
-3
lines changed

2 files changed

+38
-3
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,19 @@
3232
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3333
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3434
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
35+
import org.springframework.boot.autoconfigure.condition.ConditionalOnThreading;
3536
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
3637
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
3738
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
3839
import org.springframework.boot.autoconfigure.jmx.JmxProperties;
3940
import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
4041
import org.springframework.boot.autoconfigure.sql.init.OnDatabaseInitializationCondition;
4142
import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration;
43+
import org.springframework.boot.autoconfigure.thread.Threading;
4244
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4345
import org.springframework.boot.context.properties.PropertyMapper;
4446
import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException;
47+
import org.springframework.boot.task.SimpleAsyncTaskSchedulerBuilder;
4548
import org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder;
4649
import org.springframework.context.annotation.Bean;
4750
import org.springframework.context.annotation.Conditional;
@@ -65,6 +68,7 @@
6568
import org.springframework.messaging.rsocket.RSocketStrategies;
6669
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
6770
import org.springframework.scheduling.Trigger;
71+
import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler;
6872
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
6973
import org.springframework.scheduling.support.CronTrigger;
7074
import org.springframework.scheduling.support.PeriodicTrigger;
@@ -79,6 +83,7 @@
7983
* @author Stephane Nicoll
8084
* @author Vedran Pavic
8185
* @author Madhura Bhave
86+
* @author Yong-Hyun Kim
8287
* @since 1.1.0
8388
*/
8489
@AutoConfiguration(after = { DataSourceAutoConfiguration.class, JmxAutoConfiguration.class,
@@ -165,19 +170,31 @@ private Trigger createPeriodicTrigger(Duration period, Duration initialDelay, bo
165170
}
166171

167172
/**
168-
* Expose a standard {@link ThreadPoolTaskScheduler} if the user has not enabled task
169-
* scheduling explicitly.
173+
* Expose a standard {@link org.springframework.scheduling.TaskScheduler
174+
* TaskScheduler} if the user has not enabled task scheduling explicitly. A
175+
* {@link SimpleAsyncTaskScheduler} is exposed if the user enables virtual threads via
176+
* {@code spring.threads.virtual.enabled=true}, otherwise
177+
* {@link ThreadPoolTaskScheduler}.
170178
*/
171179
@Configuration(proxyBeanMethods = false)
172-
@ConditionalOnBean(ThreadPoolTaskSchedulerBuilder.class)
173180
@ConditionalOnMissingBean(name = IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)
174181
protected static class IntegrationTaskSchedulerConfiguration {
175182

176183
@Bean(name = IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)
184+
@ConditionalOnBean(ThreadPoolTaskSchedulerBuilder.class)
185+
@ConditionalOnThreading(Threading.PLATFORM)
177186
public ThreadPoolTaskScheduler taskScheduler(ThreadPoolTaskSchedulerBuilder threadPoolTaskSchedulerBuilder) {
178187
return threadPoolTaskSchedulerBuilder.build();
179188
}
180189

190+
@Bean(name = IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME)
191+
@ConditionalOnBean(SimpleAsyncTaskSchedulerBuilder.class)
192+
@ConditionalOnThreading(Threading.VIRTUAL)
193+
public SimpleAsyncTaskScheduler taskSchedulerVirtualThreads(
194+
SimpleAsyncTaskSchedulerBuilder simpleAsyncTaskSchedulerBuilder) {
195+
return simpleAsyncTaskSchedulerBuilder.build();
196+
}
197+
181198
}
182199

183200
/**

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import io.rsocket.transport.netty.client.TcpClientTransport;
3333
import org.assertj.core.api.InstanceOfAssertFactories;
3434
import org.junit.jupiter.api.Test;
35+
import org.junit.jupiter.api.condition.EnabledForJreRange;
36+
import org.junit.jupiter.api.condition.JRE;
3537
import reactor.core.publisher.Mono;
3638

3739
import org.springframework.beans.DirectFieldAccessor;
@@ -54,9 +56,11 @@
5456
import org.springframework.boot.sql.init.DatabaseInitializationMode;
5557
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
5658
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
59+
import org.springframework.boot.testsupport.assertj.SimpleAsyncTaskExecutorAssert;
5760
import org.springframework.context.annotation.Bean;
5861
import org.springframework.context.annotation.Configuration;
5962
import org.springframework.context.annotation.Primary;
63+
import org.springframework.core.task.SimpleAsyncTaskExecutor;
6064
import org.springframework.integration.annotation.IntegrationComponentScan;
6165
import org.springframework.integration.annotation.MessagingGateway;
6266
import org.springframework.integration.annotation.ServiceActivator;
@@ -84,6 +88,7 @@
8488
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
8589
import org.springframework.messaging.support.GenericMessage;
8690
import org.springframework.scheduling.TaskScheduler;
91+
import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler;
8792
import org.springframework.scheduling.support.CronTrigger;
8893
import org.springframework.scheduling.support.PeriodicTrigger;
8994
import org.springframework.test.util.ReflectionTestUtils;
@@ -98,6 +103,7 @@
98103
* @author Artem Bilan
99104
* @author Stephane Nicoll
100105
* @author Vedran Pavic
106+
* @author Yong-Hyun Kim
101107
*/
102108
class IntegrationAutoConfigurationTests {
103109

@@ -521,6 +527,18 @@ void integrationManagementInstrumentedWithObservation() {
521527
});
522528
}
523529

530+
@Test
531+
@EnabledForJreRange(min = JRE.JAVA_21)
532+
void integrationVirtualThreadsEnabled() {
533+
this.contextRunner.withPropertyValues("spring.threads.virtual.enabled=true")
534+
.run((context) -> assertThat(context).hasSingleBean(TaskScheduler.class)
535+
.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME, TaskScheduler.class)
536+
.isInstanceOf(SimpleAsyncTaskScheduler.class)
537+
.satisfies((taskScheduler) -> SimpleAsyncTaskExecutorAssert
538+
.assertThat((SimpleAsyncTaskExecutor) taskScheduler)
539+
.usesVirtualThreads()));
540+
}
541+
524542
@Configuration(proxyBeanMethods = false)
525543
static class CustomMBeanExporter {
526544

0 commit comments

Comments
 (0)