Java Quartz Configuration Example
The architecture of Quartz is modular and one can configure it the way they want. Configuration of Quartz is done through the use of a properties file.
See Quartz Scheduler Properties Example for more details.
Before we start with the example, I will brief you about the quartz and the setup involved.
1. What is Quartz?
Quartz is a Java open source job-scheduling system capable of scheduling and executing jobs. In this example, we use the latest stable Quartz available which is version 2.2.1. You can download Quartz from http://quartz-scheduler.org/downloads
2. Quartz Setup
In this example, we will use Maven as the build tool so all you need to do is add the below dependency to pom.xml which looks like below.
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javacodegeeks.camel</groupId> <artifactId>camelHelloWorld</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.26</version> </dependency> </dependencies> </project>
3. Quartz Configuration
The components that we will configure in this article are:
- JobStore
- DataSources
- Scheduler
- ThreadPool
4. Job Store Configuration
We have seen the various Quartz components involved in Quartz Tutorial. JobStores are responsible for keeping track of all the components: jobs, triggers, calendars, and so forth. Once you have decided the JobStore your scheduler should use, you to configure it in the quartz properties file.
Quartz comes with its own in-built JobStores. If they does not fit your needs, you can create your own by implementing the org.quar.spi.JobStore
interface.
- RAMJobStore – It keeps all of its data in RAM so once the application ends or crashes all the scheduling information is lost. Since it keeps its data in RAM, it is very fast and simple to configure
- JDBCJobStore – JDBCJobStore keeps all of its data in a database via JDBC. Since it relies on database, configuration is a bit complicated and certainly is not as fast as RAMJobStore
- TerracottaJobStore – TerracottaJobStore can be ran clustered or non-clustered, and in either case provides a storage medium for your job data that is persistent between application restarts, because the data is stored in the Terracotta server.
In our example, we will use org.quartz.simpl.RAMJobStore
. To use RAMJobStore simply set the org.quartz.jobStore.class
property to org.quartz.simpl.RAMJobStore
.
quartz.properties:
org.quartz.scheduler.instanceName=JavacodeGeeksScheduler org.quartz.scheduler.instanceId=99199 org.quartz.scheduler.rmi.export=false org.quartz.scheduler.rmi.proxy=false org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=3 org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore org.quartz.jobListener.NAME.class= com.javacodegeeks.quartz.MyJobListener
5. DataSource Configuration
If you’re using JDBC-Jobstore, you’ll be needing a DataSource for its use. Quartz created data sources are configured by providing properties in quartz.properties file.
The JdbcStore property must be set org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
. StdJDBCDelegate is a delegate that uses “vanilla” JDBC code (and SQL statements) to do its work so you need to set the driverDelegateClass org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
.
Set the datasource property, org.quartz.jobStore.dataSource=myDS
. Next define the data source properties. You also need to make sure the JdbcStore SQL is run so that it creates the quartz internal tables.
quartz.properties:
org.quartz.scheduler.instanceName=JavacodeGeeksScheduler org.quartz.scheduler.instanceId=99199 org.quartz.scheduler.rmi.export=false org.quartz.scheduler.rmi.proxy=false org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=3 org.quartz.context.key.QuartzTopic=QuartzPorperties #org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix=QRTZ_ org.quartz.jobStore.dataSource=myDS org.quartz.jobListener.NAME.class=com.javacodegeeks.quartz.MyJobListener org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost/test org.quartz.dataSource.myDS.user=admin org.quartz.dataSource.myDS.password=admin org.quartz.dataSource.myDS.maxConnections=30
6. Scheduler Configuration
You can also configure your scheduler instance. The following are some of the properties that you can use to configure the scheduler.
- org.quartz.scheduler.instanceName – You can specify any value you want, scheduler won’t do any kind of validation. It is used for the client code to distinguish schedulers when it relies on multiple schedulers withing the same program. This is of great help, if you are using the clustering features and you want to rely on a logical scheduler.
- org.quartz.scheduler.instanceId – This maintains the uniqueness for all schedulers working within a cluster. The ID can be any value. There are couple of IDs which are special to the scheduler. They are the value
AUTO
andSYS_PROP
. If it isAUTO
quartz will automatically generate an ID for you. If itSYS_PROP
, it means it is a system property and the value comes from the system propertyorg.quartz.scheduler.instanceId
. - org.quartz.scheduler.instanceIdGenerator.class – This is used only if
org.quartz.scheduler.instanceId
is set toAUTO
. By default, quartz generates the ID for you using its internal generator classorg.quartz.simpl.SimpleInstanceIdGenerator
. If you want to use a different generator, then the class name should be mentioned here. Quartz provide few more generator classes, you can write onf of your own by implementingInstanceIdGenerator
. - org.quartz.scheduler.threadName – This represents name of the Quartz java thread. If this property is not specified, the thread will derive its name from the scheduler’s name.
- org.quartz.scheduler.makeSchedulerThreadDaemon – This is a boolean value
true
orfalse
that specifies whether the main thread of the scheduler should be a daemon thread or not. - org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer – This is a boolean value
true
orfalse
that specifies whether the threads spawned by Quartz will inherit the context ClassLoader of the initializing thread. - org.quartz.scheduler.idleWaitTime – This is the wait time in milliseconds that the scheduler will use to wait before it starts to re-query for an available trigger when the scheduler is otherwise idle.
- org.quartz.scheduler.dbFailureRetryInterval – This will be used by the scheduler when it has lost the connection to JobStore database. This is the wait time of the scheduler before it tries re-connecting to the JobStore.
- org.quartz.scheduler.classLoadHelper.class – This is the helper class that Quartz uses to load a class or resource. By default it uses
org.quartz.simpl.CascadingClassLoadHelper
which in turn uses every otherClassLoadHelper
class until one works. - org.quartz.scheduler.jobFactory.class – The is class name of the
JobFactory
to use which is responsible for producing instances of Job Classes. - org.quartz.scheduler.userTransactionURL – This is the JNDI URL at which Quartz can locate the Application Server’s UserTransaction manager, the default value is
java:comp/UserTransaction
- org.quartz.scheduler.wrapJobExecutionInUserTransaction – Should be set to
true
if you want your job to be executed within a a UserTransaction. - org.quartz.scheduler.skipUpdateCheck – This is used to log extra information in log in case an updated version of Quartz is available for download. It will decide Whether or not to skip running a quick web request to determine if there is an updated version of Quartz available for download.
- org.quartz.scheduler.batchTriggerAcquisitionMaxCount – The maximum number of triggers that a scheduler node is allowed to acquire (for firing) at once. Default value is 1.
- org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow – The amount of time in milliseconds that a trigger is allowed to be acquired and fired ahead of its scheduled fire time. Defaults to 0.
7. ThreadPool Configuration
The ThreadPool provides a set of Threads for Quartz to use when executing jobs. Based on our requirement we need to configure the right number of threads. The more threads in the pool, the greater number of jobs that can run concurrently. One should make sure that they don’t end up using more threads than required as it will slow down. Remember thread creation and maintenance is not free so the basic rules are try to keep your threads minimum but make sure you have enough threads for your jobs to fire on time.
One can further specify properties based on the thread pool class chosen to set the thread pools’s properties.
Note that if a trigger’s time to fire arrives, and there isn’t an available thread, Quartz will block (pause) until a thread comes available which means the Job didn’t execute at time it is scheduled for.
Quartz ships with a simple ThreadPool named org.quartz.simpl.SimpleThreadPool. This ThreadPool simply maintains a fixed set of threads in its pool – never grows, never shrinks. This is reasonable for most of the requirements but if needed you can still create your own ThreadPool.
org.quartz.threadPool.class
– Name of the ThreadPool implementation that Quartz will use. In our example we will useorg.quartz.simpl.SimpleThreadPool
.org.quartz.threadPool.threadCount
– Number of threads available for concurrent execution of jobs. This is be any positive integer, ideally between 1 and 100.org.quartz.threadPool.threadPriority
– This can be any int between Thread.MIN_PRIORITY (which is 1) and Thread.MAX_PRIORITY (which is 10). The default is Thread.NORM_PRIORITY (5).
8. Quartz Configuration Example
Let’s look into an example that combines all the above configurations. We will have three set of jobs. We will use some set of quartz properties, run the program and then analyse the behavior.
Job1:
package com.javacodegeeks.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class Job1 implements Job { private static int count; public void execute(JobExecutionContext jobContext) throws JobExecutionException { System.out.println("--------------------------------------------------------------------"); System.out.println("Job1 start: " + jobContext.getFireTime()); count++; System.out.println("Job count " + count); System.out.println("Job1 next scheduled time: " + jobContext.getNextFireTime()); System.out.println("Job's thread name is: " + Thread.currentThread().getName()); System.out.println("Job end"); System.out.println("--------------------------------------------------------------------"); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Job2:
package com.javacodegeeks.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class Job2 implements Job { private static int count; public void execute(JobExecutionContext jobContext) throws JobExecutionException { System.out.println("--------------------------------------------------------------------"); System.out.println("Job2 start: " + jobContext.getFireTime()); count++; System.out.println("Job count " + count); System.out.println("Job2 next scheduled time: " + jobContext.getNextFireTime()); System.out.println("Job's thread name is: " + Thread.currentThread().getName()); System.out.println("Job end"); System.out.println("--------------------------------------------------------------------"); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Job3:
package com.javacodegeeks.quartz; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class Job3 implements Job { private static int count; public void execute(JobExecutionContext jobContext) throws JobExecutionException { System.out.println("--------------------------------------------------------------------"); System.out.println("Job3 start: " + jobContext.getFireTime()); count++; System.out.println("Job count " + count); System.out.println("Job3 next scheduled time: " + jobContext.getNextFireTime()); System.out.println("Job's thread name is: " + Thread.currentThread().getName()); System.out.println("Job end"); System.out.println("--------------------------------------------------------------------"); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
We will also configure a job listener.
MyJobListener:
package com.javacodegeeks.quartz; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobListener; public class MyJobListener implements JobListener { public void jobToBeExecuted(JobExecutionContext context) { System.out.println("Job to be exected: " + context.getFireInstanceId() + ", job listener: " + getName()); } public void jobExecutionVetoed(JobExecutionContext context) { } public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { System.out.println("Job was exected: " + context.getFireInstanceId() + ", job listener: " + getName()); } public String getName() { return "MyJobListener"; } }
In the example, we will first run the main program with a thread count of 1 and then modify the property to 3, and rerun the main program again.
QuartzSchedulerConfigurationExample:
package com.javacodegeeks.quartz; import org.quartz.Job; import org.quartz.JobBuilder; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.SimpleScheduleBuilder; import org.quartz.Trigger; import org.quartz.TriggerBuilder; public class QuartzSchedulerConfigurationExample { private int repeatCount = 3; public static void main(String[] args) throws Exception { QuartzSchedulerConfigurationExample quartzSchedulerExample = new QuartzSchedulerConfigurationExample(); Scheduler scheduler = quartzSchedulerExample.createAndStartScheduler(); quartzSchedulerExample.fireJob(scheduler, Job1.class); quartzSchedulerExample.fireJob(scheduler, Job2.class); quartzSchedulerExample.fireJob(scheduler, Job3.class); } public Scheduler createAndStartScheduler() throws SchedulerException { SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); Scheduler scheduler = schedFact.getScheduler(); System.out .println("Scheduler name is: " + scheduler.getSchedulerName()); System.out.println("Scheduler instance ID is: " + scheduler.getSchedulerInstanceId()); System.out.println("Scheduler context's value for key QuartzTopic is " + scheduler.getContext().getString("QuartzTopic")); scheduler.start(); return scheduler; } public <T extends Job> void fireJob(Scheduler scheduler, Class<T> jobClass) throws SchedulerException, InterruptedException { // define the job and tie it to our HelloJob class JobBuilder jobBuilder = JobBuilder.newJob(jobClass); JobDataMap data = new JobDataMap(); data.put("latch", this); JobDetail jobDetail = jobBuilder .usingJobData("example", "com.javacodegeeks.quartz.QuartzSchedulerExample") .usingJobData(data).build(); // Trigger the job to run now, and then every 40 seconds Trigger trigger = TriggerBuilder .newTrigger() .startNow() .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withRepeatCount(repeatCount) .withIntervalInSeconds(2)) .withDescription("MyTrigger").build(); // Tell quartz to schedule the job using our trigger scheduler.scheduleJob(jobDetail, trigger); } }
Here is the complete quartz properties.
quartz.properties:
org.quartz.scheduler.instanceName=JavacodeGeeksScheduler org.quartz.scheduler.instanceId=99199 org.quartz.scheduler.rmi.export=false org.quartz.scheduler.rmi.proxy=false org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=1 org.quartz.context.key.QuartzTopic=QuartzPorperties org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore #org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX #org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate #org.quartz.jobStore.tablePrefix=QRTZ_ #org.quartz.jobStore.dataSource=myDS #org.quartz.jobListener.NAME.class=com.javacodegeeks.quartz.MyJobListener #org.quartz.dataSource.myDS.driver=com.mysql.jdbc.Driver #org.quartz.dataSource.myDS.URL=jdbc:mysql://localhost/test #org.quartz.dataSource.myDS.user=admin #org.quartz.dataSource.myDS.password=admin #org.quartz.dataSource.myDS.maxConnections=30
As you can see many of the properties are commented. If you want to try the JdbcStore instead of RAMJobStore, uncomment the commented properties and comment RAMJobStore.
When you run with thread count 1, you can see below the jobs are not run concurrently and there is also a delay in the next scheduled execution.
Output:
Scheduler name is: JavacodeGeeksScheduler Scheduler instance ID is: 99199 Scheduler context's value for key QuartzTopic is null -------------------------------------------------------------------- Job1 start: Thu Oct 08 22:08:29 IST 2015 Job count 1 Job1 next scheduled time: Thu Oct 08 22:08:31 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- Job3 start: Thu Oct 08 22:08:32 IST 2015 Job count 1 Job3 next scheduled time: Thu Oct 08 22:08:31 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- Job1 start: Thu Oct 08 22:08:35 IST 2015 Job count 2 Job1 next scheduled time: Thu Oct 08 22:08:33 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- Job2 start: Thu Oct 08 22:08:38 IST 2015 Job count 1 Job2 next scheduled time: Thu Oct 08 22:08:37 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- Job2 start: Thu Oct 08 22:08:41 IST 2015 Job count 2 Job2 next scheduled time: Thu Oct 08 22:08:39 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- Job1 start: Thu Oct 08 22:08:44 IST 2015 Job count 3 Job1 next scheduled time: Thu Oct 08 22:08:46 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end --------------------------------------------------------------------
Now increase the thread count to 3. When you rerun the program you will see the jobs now run concurrently.
Output:
Scheduler name is: JavacodeGeeksScheduler Scheduler instance ID is: 99199 Scheduler context's value for key QuartzTopic is null -------------------------------------------------------------------- -------------------------------------------------------------------- -------------------------------------------------------------------- Job2 start: Thu Oct 08 22:16:16 IST 2015 Job1 start: Thu Oct 08 22:16:16 IST 2015 Job3 start: Thu Oct 08 22:16:16 IST 2015 Job count 1 Job count 1 Job count 1 Job2 next scheduled time: Thu Oct 08 22:16:18 IST 2015 Job1 next scheduled time: Thu Oct 08 22:16:18 IST 2015 Job3 next scheduled time: Thu Oct 08 22:16:18 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-2 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- Job end -------------------------------------------------------------------- Job's thread name is: JavacodeGeeksScheduler_Worker-3 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- Job1 start: Thu Oct 08 22:16:19 IST 2015 Job count 2 -------------------------------------------------------------------- Job1 next scheduled time: Thu Oct 08 22:16:20 IST 2015 Job2 start: Thu Oct 08 22:16:19 IST 2015 Job count 2 -------------------------------------------------------------------- Job's thread name is: JavacodeGeeksScheduler_Worker-3 Job end Job3 start: Thu Oct 08 22:16:19 IST 2015 Job count 2 Job3 next scheduled time: Thu Oct 08 22:16:20 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-2 Job end -------------------------------------------------------------------- Job2 next scheduled time: Thu Oct 08 22:16:20 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- -------------------------------------------------------------------- Job1 start: Thu Oct 08 22:16:22 IST 2015 Job count 3 Job1 next scheduled time: Thu Oct 08 22:16:22 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-2 Job end -------------------------------------------------------------------- -------------------------------------------------------------------- Job3 start: Thu Oct 08 22:16:22 IST 2015 Job count 3 Job3 next scheduled time: Thu Oct 08 22:16:22 IST 2015 -------------------------------------------------------------------- Job's thread name is: JavacodeGeeksScheduler_Worker-1 Job end -------------------------------------------------------------------- Job2 start: Thu Oct 08 22:16:22 IST 2015 Job count 3 Job2 next scheduled time: Thu Oct 08 22:16:22 IST 2015 Job's thread name is: JavacodeGeeksScheduler_Worker-3 Job end --------------------------------------------------------------------
9. Download the Eclipse Project
This was an example about Java Quartz Configuration.
You can download the full source code of this example here: quartzSchedulerConfigurationExample.zip