I have looked at this question for the solution to my issue but there is nothing there can can help my case, or maybe I don't completely understand the solution there.
I'm executing multiple Quartz scheduled emailing tasks which are supposed to run at application startup, as well as dynamically scheduled, rescheduled and unscheduled at runtime. Following is my config class.
@Configuration
@ComponentScan(basePackages="de.it2media.dps.statistics")
public class AppConfig{
@Autowired private AutowireCapableBeanFactory autowireCapableBeanFactory;
@Bean
public SchedulerFactoryBean schedulerFactoryBean(){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(autowiringSpringBeanFactory());
return schedulerFactoryBean;
}
@Bean
public SpringBeanJobFactory autowiringSpringBeanFactory(){
return new SpringBeanJobFactory(){
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object job = super.createJobInstance(bundle);
autowireCapableBeanFactory.autowireBean(job);
return job;
}
};
}
@Bean
public Scheduler scheduler() throws SchedulerException{
Scheduler scheduler = schedulerFactoryBean().getScheduler();
return scheduler;
}
}
I have a @PostConstruct
method in one of my @Controller
classes as below.
@Controller
@RequestMapping("/task")
public class TaskController extends BaseRemoteService implements TaskService {
@Autowired Scheduler scheduler;
@PostConstruct
private void afterPropertiesSet() throws Exception {
List<EmailTask> taskList;
taskList = getSavedTasks();
for (EmailTask emailTask : taskList) {
if (!scheduler.checkExists(new JobKey(emailTask.getTaskKey()))) {
JobDetail jobDetail = JobBuilder.newJob(EmailCronJob.class)
.withIdentity(emailTask.getTaskKey(), "emailTaskGroup").storeDurably(false).build();
jobDetail.getJobDataMap().put("emailTask", emailTask);
CronTrigger cronTrigger = TriggerBuilder.newTrigger().forJob(jobDetail)
.withIdentity(emailTask.getTaskKey(), "emailTaskGroup")
.withSchedule(CronScheduleBuilder.cronSchedule(emailTask.getCronExpression())).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}
}
}
And my web.xml
file.
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>de.it2media.dps.statistics.server.config.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>de.it2media.dps.statistics.server.config.MVCConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/DPSStatistics/rpc/*</url-pattern>
</servlet-mapping>
I have another config file that imports MVC configuration from Spring but so far I haven't had the need to delare beans in that. I don't know if it really matters but here goes.
@Configuration
@ComponentScan(basePackages="de.it2media.dps.statistics.server.controller")
@EnableWebMvc
public class MVCConfig {}
So the problem, as mentioned in the title, is that the @PostConstruct
method is called twice. And the same Quartz jobs are all attempted to create twice and I get the following exception.
[INFO] 2016/10/06 13:42:27.505 WARN [main] AnnotationConfigWebApplicationContext:549 - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskController': Invocation of init method failed; nested exception is org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'emailTaskGroup.423946369', because one already exists with this identification.
[INFO] 2016/10/06 13:42:27.510 ERROR [main] DispatcherServlet:502 - Context initialization failed
[INFO] org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'taskController': Invocation of init method failed; nested exception is org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'emailTaskGroup.423946369', because one already exists with this identification.
[INFO] at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1575) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:634) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
[INFO] at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:682) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
I reckon the issue has something to do with AutowireCapableBeanFactory
which I need to provide application context to my Scheduler class for Quartz, but I'm not sure.
I'm completely stumped.