Search This Blog

Wednesday, June 5, 2019

Quartz configuration in Spring Boot Application!!


Almost in every application which we develop , we need to implement schedulers to perform few jobs repeatedly or on some configured time basis.
Before implementation we think that its really tough stuff , and we really don't know how exactly it works. Don't worry I am going to explain it in very simple way.


Implementation:

So if you are working with a maven based project then first of all you need to include below dependencies into your project's pom.xml file.

                <dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
</dependency>

After inclusion of above dependencies you need to configure some basic properties , I am using spring boot application so here are the properties which we need to configure in 'application.properties' or you can create your own quarz.properties like below -

Properties::

#***************************************************************************************************
#*  FILE         : quartz.properties
#*  
#*  PACKAGE      : JTL QUARTZ COMPONENT CONFIGURATION PROPERTIES
#*  
#*  VERSION      : 01.03
#*  
#*  REVISION LOG : 
#*  
#***************************************************************************************************

org.quartz.scheduler.instanceId=JTLScheduler
org.quartz.scheduler.instanceName=JTLScheduler
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.tablePrefix = JTL_
#org.quartz.jobStore.tablePrefix = QRTZ_
#For DB2
#org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.DB2v6Delegate
#For Mysql
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?
org.quartz.plugin.triggHistory.class=org.quartz.plugins.history.LoggingTriggerHistoryPlugin
org.quartz.plugin.triggHistory.triggerFiredMessage=Trigger {1}.{0} fired job {6}.{5} at:{4}, date,HH:mm:ss dd/MM/yyyy}
org.quartz.plugin.triggHistory.triggerCompleteMessage=Trigger {1}.{0} completed firing job {6}.{5} at {4,date, HH:mm:ss dd/MM/yyyy} with resulting trigger instruction code:{9}
org.quartz.plugin.jobHistory.class=org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.jobHistory.jobSuccessMessage=Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=OK
org.quartz.plugin.jobHistory.jobFailedMessage=Job {1}.{0} fired at: {2, date, dd/MM/yyyy HH:mm:ss} result=ERROR


That's all about the properties you need to configure , obviously you can remove few if you don't need it .

Last but not least , you need to write a configuration class which will be loaded when your Spring application gets started .

Configuration Class:


package com.quartz.config;

import java.io.IOException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.common.errors.ApiException;
import org.quartz.Trigger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;


/**
 * The Class QuartzConfig.
 * @author bharat
 */
@Configuration
public class QuartzConfig {

    /** The data source. */
    @Autowired
    private DataSource dataSource;

    /** The transaction manager. */
    @Autowired
    private PlatformTransactionManager transactionManager;

    /** The application context. */
    @Autowired
    private ApplicationContext applicationContext;
    
    /** The cron expression. */
    @Value("${CRON_EXPRESSION}")
String cornExpression;
    
    @Value("${default.cron.expression}")
String defaultCornExpression;

    LogEventGenerator logEventGenerator = new LogEventGenerator().nodeType(Constants.MODULE_NAME)
            .logger(LoggerFactory.getLogger(QuartzConfig.class));
    
    /**
     * Quartz scheduler.
     *
     * @return the scheduler factory bean
     */
    @Bean
    public SchedulerFactoryBean quartzScheduler() {
        SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean();
        quartzScheduler.setQuartzProperties(quartzProperties());
        quartzScheduler.setDataSource(dataSource);
        quartzScheduler.setTransactionManager(transactionManager);
        quartzScheduler.setOverwriteExistingJobs(true);

        // Custom job factory of spring with DI support for @Autowired
        JTLJobFactory jobFactory = new JTLJobFactory();
        jobFactory.setApplicationContext(applicationContext);
        quartzScheduler.setJobFactory(jobFactory);

        Trigger[] triggers = {
                processJobTrigger().getObject()
        };
        quartzScheduler.setTriggers(triggers);
        return quartzScheduler;
    }

    /**
     * Process my job.
     *
     * @return the job detail factory bean
     */
    @Bean
    public JobDetailFactoryBean processJob() {
        JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
        jobDetailFactory.setJobClass(CacheRefreshJob.class);
        jobDetailFactory.setDurability(true);
        return jobDetailFactory;
    }

    /**
     * Process my job trigger.
     *
     * @return the cron trigger factory bean
     */
    @Bean
    public CronTriggerFactoryBean processJobTrigger() {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        cronTriggerFactoryBean.setJobDetail(processJob().getObject());
    String cronExp = cornExpression;
    if(StringUtils.isEmpty(cronExp)) {
    logEventGenerator.create(EventLog.JTL7106).log();
        cronExp = defaultCornExpression;
        }
        cronTriggerFactoryBean.setCronExpression(cronExp);
        return cronTriggerFactoryBean;
    }

    /**
     * Quartz properties.
     *
     * @return the properties
     */
    @Bean
    public Properties quartzProperties() {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
        Properties properties;

        try {
            propertiesFactoryBean.afterPropertiesSet();
            properties = propertiesFactoryBean.getObject();
        }
        catch (IOException e) {
        logEventGenerator.create(EventLog.JTL7107).log();
            throw new ApiException("Unable to load quartz.properties", e);
        }

        return properties;
    }

One more class you need to create for job factory settings -


/**
 * A factory for creating JTLJob objects.
 * @author bharat
 */
public class JTLJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

/** The bean factory. */
private AutowireCapableBeanFactory beanFactory;

/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}

/* (non-Javadoc)
* @see org.springframework.scheduling.quartz.SpringBeanJobFactory#createJobInstance(org.quartz.spi.TriggerFiredBundle)
*/
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job);
return job;
}
}


In the above code you may need to inject or use few other classes which you will create for your job details .

Please feel free to comment in the comment box if you are not able to implement your job using above configuration , I will help you out on this.

Note: Please don't forget to click on follow button so that you will receive a notification whenever I will post a new article here.

No comments :

Post a Comment