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!