Using Clustered Camel Quartz Jobs on JBoss EAP

Camel Quartz can be a useful component for jobs that need to run at a particular time every day. Recently on a client site, we had a need for about 15 different jobs that each created a differently formatted file and send each file to a particular destination. While this was straightforward to get setup on a single machine, once we started deploying our camel routes to multiple servers the jobs started to kick off on both machines. To resolve this issue we needed to create a job store.

Step 1

First, create your camel routes and add the camel-quartz dependency to your project. Ensure you use the job.name option on your endpoints. This will make it clearer where things are stored in the job store. For using the oracle DB we needed the following dependencies.

<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc6</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-quartz2</artifactId>
</dependency>

A sample route looks like the following:

from("quartz2://mySampleCronJob?cron=0+0+16+*+*+?&trigger.timeZone=America/Chicago&job.name=myCronJob")
    .log(LoggingLevel.INFO, "Cron job kicked off");

Step 2

The next step in creating this job store was creating the tables. You will need to create qrtz_job_details, qrtz_triggers, qrtz_simple_triggers, qrtz_cron_triggers, qrtz_simprop_triggers, qrtz_blob_triggers, qrtz_calendars, qrtz_paused_trigger_grps_ qrtz_fired_triggers, qrtz_scheduler_state, and qrtz_locks in addition to some indexes. The script for your particular database can be found here http://www.quartz-scheduler.org/downloads/ inside the quartz-2.2.3/docs/dbTables folder.

Step 3

Finally, you need to configure your quartz.properties. If you are planning to only be in one environment, feel free to use a properties file named quartz.properties. However, in our case, we had multiple environments to account for and in turn a different database in each. With spring you can configure a bean for your quartz camel component with all the properties needed similar to below. Note that SPRING_ACTIVE_PROFILE is a system property that can be set on your EAP instance. From there, the code will look at the corresponding property file such as uat.properties. This file should then contain the database configuration values of db.urldb.user, and db.password.

@Configuration
@PropertySource(value={"classpath:${SPRING_ACTIVE_PROFILE}.properties"})
public class SampleConfig{

    @Value("${db.url}")
    private String dbUrl;
    
    @Value("${db.user}")
    private String dbUser;

    @Value("${db.password}")
    private String dbPassword;

    public SampleConfig(){}

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
         return new PropertySourcesPlaceholderConfigurer();
    }
  
    @Bean
    public QuartzComponent quartz2(){
        QuartzComponent qc = new QuartzComponent();
        Properties p = new Properties();
        p.put("org.quartz.scheduler.instanceName", "myScheduler");
        p.put("org.quartz.scheduler.instanceId", "AUTO");
        p.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        p.put("org.quartz.threadPool.threadCount", "25");
        p.put("org.quartz.threadPool.threadPriority", "5");
        p.put("org.quartz.jobStore.misfireThreshold", "60000");
        p.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
        p.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate");
        p.put("org.quartz.jobStore.useProperties", "false");
        p.put("org.quartz.jobStore.dataSource", "myDS");
        p.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        p.put("org.quartz.jobStore.isClustered", "true");
        p.put("org.quartz.jobStore.clusterCheckinInterval", "20000");
        p.put("org.quartz.dataSource.myDS.driver", "oracle.jdbc.OracleDriver");
        p.put("org.quartz.dataSource.myDS.maxConnections", "5");
        p.put("org.quartz.dataSource.myDS.validationQuery", "select 0 from dual");
        //ENV specific
        p.put("org.quartz.dataSource.myDS.URL", dbUrl);
        p.put("org.quartz.dataSource.myDS.user", dbUser);
        p.put("org.quartz.dataSource.myDS.password", dbPassword);
        qc.setProperties(p);
        return qc;
    }
}

Tips and Tricks

  • The first time you deploy to two servers in the same environment, you will see the tables such as qrtz_job_details and qrtz_cron_triggers are populated. One row for each job/route regardless of deploying twice. Upon redeployment of the same version, the row may be replaced if you change something. Note though that upon deployment of a new version a new row for each job/route will be added. Do not worry, quartz is able to keep track of which row is the most recent version and will only kick off the job once still.
  • Start with ensuring a single local environment works and populates your job store before moving onto clustered environments.
  • Configure your camel quartz component directly to ensure it is getting the correct properties whether through a quartz properties file or through java.util.properties.
  • Clear out the following in eap before deploying this configuration for the first time. jboss-eap/standalone/tmp, jboss-eap/standalone/data, jboss-eap/standalone/logs, as well as the deployment section of standalone.xml or the corresponding folders and config files for whatever configuration you are using.

Click here and quickly get started with the JBoss EAP download.

Share