Zire
Zire

Reputation: 71

Autowiring in Quartz multiple Jobs with Spring Boot not working

I'm try to use quartz scheduler in spring. I get below exception when configuring multiple jobs

Parameter 0 of method jobTrigger in Job2 required a bean of type 'org.quartz.JobDetail' that could not be found.

quartz - v2.3, Spring - v4.2.x

Config Class

@Configuration
 public class SchedulerConfig {

private static final Logger LOG = LoggerFactory.getLogger(SchedulerConfig.class);

@Autowired
List<Trigger> triggers;

@Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
    AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(applicationContext);
    return jobFactory;
}

@Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory)  throws IOException {
    SchedulerFactoryBean factory = new SchedulerFactoryBean();
          factory.setAutoStartup(true);
    factory.setJobFactory(jobFactory);
         factory.setQuartzProperties(quartzProperties());
    if (triggers != null && !triggers.isEmpty()) {
         LOG.info("starting jobs... Total Triggers - " + triggers.size());
        factory.setTriggers(triggers.toArray(new Trigger[triggers.size()]));
    }

    return factory;
}

@Bean
public Properties quartzProperties() throws IOException {
    PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
    propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
    propertiesFactoryBean.afterPropertiesSet();
    return propertiesFactoryBean.getObject();
}


public static CronTriggerFactoryBean createCronTrigger(JobDetail jobDetail, String cronExpression) {
    CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
    factoryBean.setJobDetail(jobDetail);
    factoryBean.setCronExpression(cronExpression);
    factoryBean.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
    return factoryBean;
}

public static JobDetailFactoryBean createJobDetail(Class jobClass) {
    JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
    factoryBean.setJobClass(jobClass);
    factoryBean.setDurability(true);
    return factoryBean;
}

SpringBeanJobFactory

public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

private static final Logger LOG = LoggerFactory.getLogger(AutowiringSpringBeanJobFactory.class);

private transient AutowireCapableBeanFactory beanFactory;

@Override
public void setApplicationContext(final ApplicationContext context) {
    beanFactory = context.getAutowireCapableBeanFactory();
}

@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
    final Object job = super.createJobInstance(bundle);
    LOG.info("create job instance");
    beanFactory.autowireBean(job);
    return job;
}

}

Job 1

@Component
@DisallowConcurrentExecution
public class Job1 implements Job {

private final Logger log = LoggerFactory.getLogger(this.getClass());

@Value("${schedule}")
private String frequency;

@Autowired
private Service service;

@Override
public void execute(JobExecutionContext jobExecutionContext) {
    log.info("execute");
}

@Bean(name = "jobBean1")
public JobDetailFactoryBean job() {
    return SchedulerConfig.createJobDetail(this.getClass());
}

@Bean(name = "jobBean1Trigger")
public CronTriggerFactoryBean jobTrigger(@Qualifier("jobBean1")JobDetail jobDetail) {
    return SchedulerConfig.createCronTrigger(jobDetail, frequency);
}

Job 2

@Component
@DisallowConcurrentExecution
public class Job2 implements Job {

private final Logger log = LoggerFactory.getLogger(this.getClass());

@Value("${schedule}")
private String frequency;

@Autowired
private Service service;

@Override
public void execute(JobExecutionContext jobExecutionContext) {
    log.info("execute");
}

@Bean(name = "jobBean2")
public JobDetailFactoryBean job() {
    return SchedulerConfig.createJobDetail(this.getClass());
}

@Bean(name = "jobBean2Trigger")
public CronTriggerFactoryBean jobTrigger(@Qualifier("jobBean2")JobDetail jobDetail) {
    return SchedulerConfig.createCronTrigger(jobDetail, frequency);
}

The Service class has Spring JPA repos. The root cause of the problem is the below autowired service. If I remove the below autowired service from both the jobs, it works fine.

@Autowired private Service service;

If there is only one job with this autowired bean, then there is no exception. How to configure multiple jobs using the same autowired dependency? What is cause this issue?

Upvotes: 7

Views: 16030

Answers (4)

Musaddique S
Musaddique S

Reputation: 1567

I had the same issue, but it got resolved now

package com.crif.credity.config;

import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;

public class SchedulerJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
    private AutowireCapableBeanFactory beanFacotry;

    @Override
    protected Object createJobInstance(TriggerFiredBundle triggerBundle) throws Exception {
        Object createJobInstance = super.createJobInstance(triggerBundle);
        beanFacotry.autowireBean(createJobInstance);
        return createJobInstance;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        beanFacotry = applicationContext.getAutowireCapableBeanFactory();
    }

}

in configuration java file you can create below bean

@Autowired
    private ApplicationContext applicationContext;

/** The data source. */
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerJobFactory jobFactory = new SchedulerJobFactory();
        jobFactory.setApplicationContext(applicationContext);


        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        factoryBean.setOverwriteExistingJobs(true);     
        factoryBean.setJobFactory(jobFactory);
        return factoryBean;
    }

after this in implementation of job class . you can use autowired

package com.crif.credity.jobs;




public class Test implements Job  {

    @Autowired 
    private BatchClientService batchClientService;



    @Override
public void execute(JobExecutionContext context) throws JobExecutionException{
        try {
            batchClientService.executeJob(CredityJob.NEWSCHECK, instanceId);
        } catch (Exception ex) {
            LOG.info(ex.getMessage());
        }
    }

}

Upvotes: 0

Rob Cullen
Rob Cullen

Reputation: 71

This is a modified version of the http://www.baeldung.com/spring-quartz-schedule that you referenced for handling multiple Quartz Jobs in one configuration file. For brevity I'm not including the entire QrtzSheduler class just the replacement for the scheduler method and the use of @Qualifier reference in the Triggers:

...
@Bean
public Scheduler scheduler(Map<String, JobDetail> jobMap, Set<? extends Trigger> triggers) throws SchedulerException, IOException {

    StdSchedulerFactory factory = new StdSchedulerFactory();
    factory.initialize(new ClassPathResource("quartz.properties").getInputStream());

    logger.debug("Getting a handle to the Scheduler");
    Scheduler scheduler = factory.getScheduler();
    scheduler.setJobFactory(springBeanJobFactory());
    Map<JobDetail,Set<? extends Trigger>> triggersAndJobs = new HashMap<JobDetail,Set<? extends Trigger>>;
    for(JobDetail jobDetail : jobMap.getValues()){
        for(Trigger trigger : triggers){
            if(trigger.getJobKey().equals(jobDetail.getKey())){
               Set<Trigger> set = new HashSet<>();
               set.add(trigger);
               triggerAndJobs.put(jobDetail,set);
            }
        }
    }
    scheduler.scheduleJobs(triggersAndJobs, false);

    logger.debug("Starting Scheduler threads");
    scheduler.start();
    return scheduler;
}

@Bean(name="jobOne")
public JobDetail jobDetailOne() {

    ...
}

@Bean(name="jobTwo")
public JobDetail jobDetailTwo() {

   ...
}

@Bean
public Trigger triggerOne(@Qualifier("jobOne")JobDetail jobDetail) {

   ...
}

@Bean
public Trigger triggerTwo(@Qualifier("jobTwo")JobDetail jobDetail) {

   ...
}

Upvotes: 7

NiVeR
NiVeR

Reputation: 9806

I faced the same issue and after a bit of struggling I was able to resolve it. It is probably realted to the annotation that is used in the job class. I see that you are using the @Component, as it was in my case. The quartz scheduler configuration instead, is annotated with @Configuration tag.

The solution is to annotate your jobs with @Configuration tag. My guess is that the @Bean's constructed in @Componentannotated classes are not completley constructed/mapped in the phase where @Configuration annotated ones are initialized.

Upvotes: 1

Shanoj S
Shanoj S

Reputation: 11

You are trying to use spring boot configuration with Spring 4.2.

Try changing the following methods inside Job class as follows

@Bean(name = "jobBean1")
public JobDetail job() {
    return SchedulerConfig.createJobDetail(this.getClass()).getObject();
}

@Bean(name = "jobBean1Trigger")
public CronTrigger jobTrigger(@Qualifier("jobBean1")JobDetail jobDetail) {
    return SchedulerConfig.createCronTrigger(jobDetail, frequency).getObject();
}

Also use spring 4.3 since you need

@Autowired
List<Trigger> triggers;

I believe Collection Autowire works only in 4.3

Upvotes: 1

Related Questions