tomas lin opened SPR-11498 and commented
We are trying to incorporate spring websockets into a Spring Boot application with a custom implementation that refreshes the SpringApplication.
It seems that the implementation of SockJS instantiates two Task Schedulers: [defaultSockJsTaskScheduler, messageBrokerSockJsTaskScheduler]
If spring-websocket is disabled, everything seems to work fine. However, we run into a one TaskScheduler limitation of ScheduledAnnotationBeanPostProcessor whenever we try to refresh once we include the websocket functionality.
It would be nice if spring-websockets played nice with SpringApplication.refresh.
The error log is attached.
2014-02-28 15:12:37,956 ERROR com.netflix.server.base.BaseServer$UncaughtExceptionHandlerImpl:684 [main] [uncaughtException] from main java.lang.IllegalStateException: More than one TaskScheduler and/or ScheduledExecutorService exist within the context. Remove all but one of the beans; or implement the SchedulingConfigurer interface and call ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback. Found the following beans: [defaultSockJsTaskScheduler, messageBrokerSockJsTaskScheduler] at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:289) at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:72) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:98) at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:333) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:776) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:142) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:485) at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:120) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:616)
Affects: 4.0.2
Comment From: spring-projects-issues
Rossen Stoyanchev commented
It seems, from the presence of ScheduledAnnotationBeanPostProcessor, that you have an @EnableScheduling in your configuration? This PostProcessor enables use of the @Scheduled annotation and needs a TaskScheduler. It tries to find one in the configuration. If it finds 1 it uses it, if it finds more than 1 it raises the above exception, or if it finds 0 it creates a default one (backed by single-threaded executor).
The presence of defaultSockJsTaskScheduler and messageBrokerSockJsTaskScheduler indicates you probably have both @EnableWebSocket and @EnableWebSocketMessageBroker. Spring Boot's WebSocketAutoConfiguration is likely pulling in the former and my guess that you have @EnableWebSocketMessageBroker in your own configuration. The result is that the above PostProcessor finds two task schedulers and doesn't know which one you want it to use.
Well, I've made a lot of guesses but let's suppose this is more or less the situation..
I recommend turning off Boot's WebSocketAutoConfiguration unless you're using it (via @EnableAutoConfiguration's(exclude=WebSocketAutoConfiguration.class). Then you'll have only one scheduler (and the exception should go away).
If you're okay with using the SockJS task scheduler (session heartbeats and expired session cleanup) for your @Scheduled methods too, then there nothing further to do, although it's highly recommended to configure the thread pool backing the SockJS task scheduler. Alternatively if you want to use a separate task scheduler for your @Scheduled methods, then implement SchedulingConfigurer and configure it explicitly as described in the javadoc for @Scheduled. In fact this might be a good idea regardless, in order to remove any possible ambiguity now or in the future.
Comment From: spring-projects-issues
Rossen Stoyanchev commented
BTW I've commented on this Spring Boot ticket regarding the WebSocket auto configuration.
Comment From: spring-projects-issues
tomas lin commented
Thanks for this. It makes sense. Just to note, I enabled the @EnableWebSocketMessageBroker by following the guide at http://spring.io/guides/gs/messaging-stomp-websocket/ . If this is not best practice, that guide should also be updated to exclude the WebSocketAutoConfiguration
Comment From: spring-projects-issues
Rossen Stoyanchev commented
Good point and good to know. We will look for a way to sort this out on both levels, Spring Boot and the guide.
Comment From: spring-projects-issues
Greg Turnquist commented
@rossen When the dust settles, are you willing to update the websocket guide so it contains the proper solution?
Comment From: switlen
when i use both @EnableWebSocket and @EnableWebSocketMessageBroker annotation at two config class with 4.2.x spring,and i have @EnableScheduling config class,it occur NoUniqueBeanDefinitionException,i find it has been fixed more than 4.3 version,method finishRegistration() has removed the IllegalStateException,so higher spring version will resolve this problem!