Skip to content

use spring-boot-starter-quartz and spring-boot-starter-websocket simultaneously #24448

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
felixu1992 opened this issue Dec 10, 2020 · 8 comments
Labels
for: external-project For an external project and not something we can fix

Comments

@felixu1992
Copy link

I want to use spring-boot-starter-quartz and spring-boot-starter-websocket simultaneously,but it fails to satisfy my needs, and when I try to use them,some errors as follow appeared.

2020-12-08 18:50:09.535 [main] ERROR org.springframework.boot.SpringApplication [line:837] - Application run failed
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'defaultSockJsTaskScheduler' is expected to be of type 'org.springframework.scheduling.TaskScheduler' but was actually of type 'org.springframework.beans.factory.support.NullBean'
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:399)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:227)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1175)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1142)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.resolveSchedulerBean(ScheduledAnnotationBeanPostProcessor.java:327)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:256)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:233)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:105)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:404)
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:361)
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:898)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:554)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)

I have figured out what caused these errors. It is because that there is a logical conflict on the instantiation of Taskscheuler after the procedure of use spring-boot-starter-quartz and spring-boot-starter-websocket simultaneously. spring-wesocket created a NullBean as instantiation of Taskscheduler. when the quartz is applied,the errors would emerge that is:
In org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport#defaultSockJsTaskScheduler

org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport#defaultSockJsTaskScheduler

@Bean
@Nullable
public TaskScheduler defaultSockJsTaskScheduler() {
	if (initHandlerRegistry().requiresTaskScheduler()) {
		ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler();
		threadPoolScheduler.setThreadNamePrefix("SockJS-");
		threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors());
		threadPoolScheduler.setRemoveOnCancelPolicy(true);
		this.scheduler = threadPoolScheduler;
	}
	return this.scheduler;
}

If this condition is not satisfied, it's going to create an instance(NullBean(org.springframework.beans.factory.support.NullBean)) of TaskScheduler here.
So we're going to rely on spring-boot-starter-quartz:

org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration#taskScheduler

@Bean
@ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class, ScheduledExecutorService.class })
public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
	return builder.build();
}

But due to the existence of the Nullbean the above method is impracticable, neither for the creation of a new workable Taskscheduler, but this problem can be tackled in this way:

@Bean
public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
    return builder.build();
}

Or implement WebSocketConfigurer to solve it.
Although we can use the above ways to settle the errors, but they are only temporary .In my opinion,the root cause for the problem is the bug of spring-websocket and so that cause the wrong existence of Nullbean
I think we can use these ways to deal with:

  • Change logical order of the two procedures, that is to put the spring-boot-starter-quartz before the instantiation
  • In spring-websocket, conditionally include TaskScheduler
  • Always create a TaskScheduler Bean without if{}
  • In spring-websocket, org.springframework.web.socket.config.annotation.WebSocketConfigurationSupport#defaultSockJsTaskScheduler no longer as a Spring Bean

The above mentioned are my immature thoughts ,may be you can solve the errors in a better way,thanks for your attention

@bclozel bclozel transferred this issue from spring-projects/spring-framework Dec 10, 2020
@bclozel bclozel added the status: waiting-for-triage An issue we've not yet triaged label Dec 10, 2020
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Dec 10, 2020

Previously we had a no-op scheduler and changed it in spring-projects/spring-framework#20737 so that now it return null. This makes it look like there is none declared for the purpose of autowiring but here it affects @ConditionalOnMissingBean. Always declaring it is guaranteed to cause issues by conflicting with other declarations. I just don't see anything we can do on the WebSocketConfigurationSupport side since there is no way to create it conditionally at the Spring Framework level, which is what it's trying to do.

Could anything be done on the Boot side for this, for example to improve how ConditionalOnMissingBean works for this scenario where a null was returned for a bean?

@snicoll
Copy link
Member

snicoll commented Dec 10, 2020

@felixu1992 thanks for the report. Could you please share a small sample that demonstrates the problem you've described? You can do so by attaching a zip to this issue or sharing a link to a GitHub repository.

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Dec 10, 2020
@felixu1992
Copy link
Author

@snicoll Thank you for your reply,this is a small sample. You can get it form https://oss-blog.felixu.top/demo.zip

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 11, 2020
@snicoll
Copy link
Member

snicoll commented Dec 11, 2020

Thanks for the minimal sample @felixu1992

I think this is could be a framework problem after all. With a default @Scheduled setup, we're going to detect the TaskScheduler by type. While that NullBean is not meant to be injected by type, org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveNamedBean will still pick it up for some reason and fail as indicated in the exception above @jhoeller any idea about that?

@ConditionalOnMissingBean uses beanFactory.getBeanNamesForType and it does back off indeed due to the presence of that NullBean:

   TaskSchedulingAutoConfiguration#taskScheduler:
      Did not match:
         - @ConditionalOnMissingBean (types: org.springframework.scheduling.annotation.SchedulingConfigurer,org.springframework.scheduling.TaskScheduler,java.util.concurrent.ScheduledExecutorService; SearchStrategy: all) found beans of type 'org.springframework.scheduling.TaskScheduler' defaultSockJsTaskScheduler (OnBeanCondition)

@felixu1992
Copy link
Author

@snicoll Thank you for your attention,and feel glad to share thoughts with you.

@snicoll
Copy link
Member

snicoll commented Dec 15, 2020

After a discussion with Juergen, we concluded that something should be done at the framework level. I've created spring-projects/spring-framework#26271

@snicoll snicoll added the status: on-hold We can't start working on this issue yet label Dec 15, 2020
@felixu1992
Copy link
Author

After a discussion with Juergen, we concluded that something should be done at the framework level. I've created spring-projects/spring-framework#26271

I think so, too, so I started this issue on the Spring Framework, but it was transferred to Spring Boot.

@snicoll
Copy link
Member

snicoll commented Dec 17, 2020

This issue is now superseded by spring-projects/spring-framework#26271. I've tried the fix and it works as expected now.

@felixu1992 you can also give it a try using Spring Framework 5.3.3-SNAPSHOT. The fix will be included in the next release (2.4.2).

@snicoll snicoll closed this as completed Dec 17, 2020
@snicoll snicoll added for: external-project For an external project and not something we can fix and removed status: feedback-provided Feedback has been provided status: on-hold We can't start working on this issue yet status: waiting-for-triage An issue we've not yet triaged labels Dec 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: external-project For an external project and not something we can fix
Projects
None yet
Development

No branches or pull requests

5 participants