Reputation: 2789
In upgrading a Spring Boot 2 application to Spring Boot 3 (Spring 5 to Spring 6) it fails to start due to WebSocket configuration.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// ---------------------------------
// This works in Spring Boot 2 and 3
// ---------------------------------
config.enableSimpleBroker("/topic");
// -------------------------------------------------------
// This works in Spring Boot 2, but fails in Spring Boot 3
// -------------------------------------------------------
config.enableSimpleBroker("/topic")
.setHeartbeatValue(new long[] { 10000, 10000 })
.setTaskScheduler(new DefaultManagedTaskScheduler());
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket");
}
}
Trying to set the heartbeat and task scheduler fails with the following information in Spring Boot 3 (Spring 6).
INFO 19002 --- [demo] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
INFO 19002 --- [demo] [ main] o.s.m.s.b.SimpleBrokerMessageHandler : Starting...
INFO 19002 --- [demo] [ main] o.s.m.s.b.SimpleBrokerMessageHandler : BrokerAvailabilityEvent[available=true, SimpleBrokerMessageHandler [org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry@49a6f486]]
WARN 19002 --- [demo] [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'simpleBrokerMessageHandler'
INFO 19002 --- [demo] [ main] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
ERROR 19002 --- [demo] [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'simpleBrokerMessageHandler'
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:288) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:469) ~[spring-context-6.1.11.jar:6.1.11]
at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:257) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:202) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:990) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:628) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) ~[spring-boot-3.3.2.jar:3.3.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) ~[spring-boot-3.3.2.jar:3.3.2]
at com.example.demo.DemoApplication.main(DemoApplication.java:10) ~[classes/:na]
Caused by: java.lang.IllegalStateException: No ScheduledExecutor is configured
at org.springframework.scheduling.concurrent.ConcurrentTaskScheduler.getScheduledExecutor(ConcurrentTaskScheduler.java:174) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.scheduling.concurrent.ConcurrentTaskScheduler.scheduleWithFixedDelay(ConcurrentTaskScheduler.java:275) ~[spring-context-6.1.11.jar:6.1.11]
at org.springframework.messaging.simp.broker.SimpleBrokerMessageHandler.startInternal(SimpleBrokerMessageHandler.java:271) ~[spring-messaging-6.1.11.jar:6.1.11]
at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.start(AbstractBrokerMessageHandler.java:227) ~[spring-messaging-6.1.11.jar:6.1.11]
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:285) ~[spring-context-6.1.11.jar:6.1.11]
... 13 common frames omitted
Any ideas how to fix this? Specifically how to set heartbeat values for WebSockets in Spring Boot 3 (Spring 6)?
Upvotes: 0
Views: 182
Reputation: 2789
Thanks to @JorgeCampos for suggestions that helped move me to the correct answer.
The problem was using DefaultManagedTaskScheduler
as the task scheduler. According to Stephane Nicoll from the Spring Boot team:
DefaultManagedTaskScheduler
should be used if you want to lookup the executor via JNDI. We're wondering if you've used this class thinking that it was the "default scheduler" given its name, can you let us know? If you used it thinking it was the default scheduler, ThreadPoolTaskScheduler is probably what you should be using.
According to the GitHub issue, it appears this changed from Spring Boot 3.1.5 to 3.2.0. I'm now using the following to get WebSockets working in Spring Boot 3.3.2. Specifically creating a custom ThreadPoolTaskScheduler
bean that gets included as the task scheduler.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic")
.setHeartbeatValue(new long[] { 10000, 10000 })
.setTaskScheduler(webSocketMessageBrokerTaskScheduler());
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket");
}
@Bean
public ThreadPoolTaskScheduler webSocketMessageBrokerTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(1);
taskScheduler.setThreadNamePrefix("wss-heartbeat-thread-");
taskScheduler.initialize();
return taskScheduler;
}
}
Upvotes: 1