Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to persist Quartz triggers in database

I'm quite new to Quartz and now I need to schedule some jobs in Spring web application.

I know about Spring + Quartz integration (I'm using Spring v 3.1.1) but I'm wondering if this is the right way to follow.

In particular I need to persist my scheduled tasks in a DB so I can re-initialize them when application is restarted.

Are there some utilities provided by Spring scheduling wrapper to do this? Can you suggest me some "well known" approach to follow?

like image 875
davioooh Avatar asked Jul 01 '13 10:07

davioooh


2 Answers

Here is one way I handle this scenario.

First in my Spring Configuration I specify a SchedulerFactoryBean from which I can inject the Scheduler into other beans.

<bean name="SchedulerFactory"
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="applicationContextSchedulerContextKey">
        <value>applicationContext</value>
    </property>
</bean>

Then when I create a job in my application I store the details of the job in the database. This service is called by one of my controllers and it schedules the job:

@Component
public class FollowJobService {

    @Autowired
    private FollowJobRepository followJobRepository;

    @Autowired
    Scheduler scheduler;

    @Autowired
    ListableBeanFactory beanFactory;

    @Autowired
    JobSchedulerLocator locator;

    public FollowJob findByClient(Client client){
        return followJobRepository.findByClient(client);
    }

    public void saveAndSchedule(FollowJob job) {
        job.setJobType(JobType.FOLLOW_JOB);
        job.setCreatedDt(new Date());
        job.setIsEnabled(true);
        job.setIsCompleted(false);

        JobContext context = new JobContext(beanFactory, scheduler, locator, job);
        job.setQuartzGroup(context.getQuartzGroup());
        job.setQuartzName(context.getQuartzName());

        followJobRepository.save(job);

        JobSchedulerUtil.schedule(new JobContext(beanFactory, scheduler, locator, job));
    }
}

The JobContext I build contains detail about the job and is eventually passed to a utility for scheduling jobs. Here is that code for the utility method that actually schedules the job. Notice that in my service I autowire the JobScheduler and pass it to the JobContext. Also notice that I store the job in the database using my repository.

/**
 * Schedules a DATA_MINING_JOB for the client. The job will attempt to enter
 * followers of the target into the database.
 */
@Override
public void schedule(JobContext context) {
    Client client = context.getNetworkSociallyJob().getClient();
    this.logScheduleAttempt(context, client);

    JobDetail jobDetails = JobBuilder.newJob(this.getJobClass()).withIdentity(context.getQuartzName(), context.getQuartzGroup()).build();
    jobDetails.getJobDataMap().put("job", context.getNetworkSociallyJob());
    jobDetails.getJobDataMap().put("repositories", context.getRepositories());

    Trigger trigger = TriggerBuilder.newTrigger().withIdentity(context.getQuartzName() + "-trigger", context.getQuartzGroup())
            .withSchedule(cronSchedule(this.getSchedule())).build();

    try {
        context.getScheduler().scheduleJob(jobDetails, trigger);            
        this.logSuccess(context, client);

    } catch (SchedulerException e) {
        this.logFailure(context, client);
        e.printStackTrace();
    }
}

So after all of this code executes I two things have happened, my job is store in the database and its been scheduled using the quartz scheduler. Now if the application restarts I want to reschedule my jobs with the scheduler. To do this I register a bean that implements ApplicationListener<ContextRefreshedEvent> which is called by Spring each time the container restarts or is started.

<bean id="jobInitializer" class="com.network.socially.web.jobs.JobInitializer"/>

JobInitializer.class

public class JobInitializer implements ApplicationListener<ContextRefreshedEvent> {

    Logger logger = LoggerFactory.getLogger(JobInitializer.class);

    @Autowired
    DataMiningJobRepository repository;

    @Autowired
    ApplicationJobRepository jobRepository;

    @Autowired
    Scheduler scheduler;

    @Autowired
    JobSchedulerLocator locator;

    @Autowired
    ListableBeanFactory beanFactory;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        logger.info("Job Initilizer started.");

        //TODO: Modify this call to only pull completed & enabled jobs
        for (ApplicationJob applicationJob : jobRepository.findAll()) {
            if (applicationJob.getIsEnabled() && (applicationJob.getIsCompleted() == null || !applicationJob.getIsCompleted())) {
                JobSchedulerUtil.schedule(new JobContext(beanFactory, scheduler, locator, applicationJob));
            }
        }       
    }

}

This class autowires the scheduler and a repository that grabs instances of each of my jobs that implement the ApplicationJob interface. Using the information from these database records I can use my scheduler utility to reconstruct my jobs.

So basically I manually store the jobs in my database and manually schedule them by injecting a instance of the Scheduler in appropriate beans. To rescheduled them, I query my database and then schedule them using the ApplicationListener to account for restarts and starts of the container.

like image 164
Kevin Bowersox Avatar answered Sep 20 '22 23:09

Kevin Bowersox


I suppose there are quite some documentation available for Spring and Quartz JDBC job store integration; for instance:

  • If you're using annotation configuration: http://java.dzone.com/articles/configuring-quartz
  • If you're using XML configuration: http://arkuarku.wordpress.com/2011/01/06/spring-quartz-using-jdbcjobstore-simple/
  • Quartz Job Stores: http://quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-09
  • Spring 3.x SchedulerFactoryBean for Quartz
like image 27
nobeh Avatar answered Sep 20 '22 23:09

nobeh