Quartz

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:

  1. JobStore
  2. DataSources
  3. Scheduler
  4. 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.

  1. 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
  2. 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
  3. 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.

  1. 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.
  2. 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 and SYS_PROP. If it is AUTO quartz will automatically generate an ID for you. If it SYS_PROP, it means it is a system property and the value comes from the system property org.quartz.scheduler.instanceId.
  3. 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 implementing InstanceIdGenerator.
  4. 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.
  5. org.quartz.scheduler.makeSchedulerThreadDaemon – This is a boolean value true or false that specifies whether the main thread of the scheduler should be a daemon thread or not.
  6. org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer – This is a boolean value true or falsethat specifies whether the threads spawned by Quartz will inherit the context ClassLoader of the initializing thread.
  7. 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.
  8. 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.
  9. 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 other ClassLoadHelper class until one works.
  10. org.quartz.scheduler.jobFactory.class – The is class name of the JobFactory to use which is responsible for producing instances of Job Classes.
  11. 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
  12. org.quartz.scheduler.wrapJobExecutionInUserTransaction – Should be set to true if you want your job to be executed within a a UserTransaction.
  13. 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.
  14. 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.
  15. 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.

  1. org.quartz.threadPool.class – Name of the ThreadPool implementation that Quartz will use. In our example we will use org.quartz.simpl.SimpleThreadPool.
  2. org.quartz.threadPool.threadCount – Number of threads available for concurrent execution of jobs. This is be any positive integer, ideally between 1 and 100.
  3. 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.

Download
You can download the full source code of this example here: quartzSchedulerConfigurationExample.zip

Ram Mokkapaty

Ram holds a master's degree in Machine Design from IT B.H.U. His expertise lies in test driven development and re-factoring. He is passionate about open source technologies and actively blogs on various java and open-source technologies like spring. He works as a principal Engineer in the logistics domain.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button