1

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.

Community
  • 1
  • 1
CobaltBabyBear
  • 2,188
  • 6
  • 26
  • 47

1 Answers1

0

@M. Deinum was right. The only thing I did was to exclude the controller package from the @ComponentScan in AppConfig.java and it works.

@ComponentScan(basePackages="de.it2media.dps.statistics", 
    excludeFilters = { @Filter(Configuration.class), @Filter(Controller.class) })
CobaltBabyBear
  • 2,188
  • 6
  • 26
  • 47