Quartz

Java Quartz with MySQL Example

1. Introduction

In this article, we will go through the configuration needed to setup MySQL, the most famous open source database, with Quartz, a very popular open source job scheduling library that can be used in Java applications. The main concept of Quartz is that a scheduler holds a list of jobs that are triggered at specific times or repeatedly. In the code examples we will see how to persist this data in MySQL.

2. Project Setup

To run the code examples of this post, we will use the following technologies:

  • Java 8
  • MySQL Server 5.7.26
  • Quartz 2.2.1
  • MySQL JDBC 5.1.34
  • SLF4J 1.7.26
  • Logback 1.2.3
  • Maven 3.3.3
  • Eclipse 4.10.0

2.1 Install MySQL Server

To run the code examples we will have to install the MySQL Community Server. From this link, download the MySQL Community Server 5.7.26 for your operating system and follow the installation instructions. Once MySQL is installed, you should be prompted to change the root password. Do not forget that password as it will be the only way to login to MySQL. Finally, MySQL should automatically start as a service.

2.2 Create a new Maven Project

In Eclipse, create a new Maven Project and add the following dependencies to the pom.xml:

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</groupId>
	<artifactId>java-quartz-mysql</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<description>Java Quartz with MySQL Example</description>
	
	<properties>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.source>1.8</maven.compiler.source>
    </properties>

	<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.34</version>
	    </dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.26</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<version>1.2.3</version>
		</dependency>
	</dependencies>
</project>

3. Create the Quartz database

The first step, before running the code examples, is to create the database and tables required by Quartz to persist data. From the the MySQL Command-Line Client execute the following commands:

pom.xml

CREATE DATABASE IF NOT EXISTS QUARTZ_SCHEMA;

USE QUARTZ_SCHEMA;

CREATE TABLE IF NOT EXISTS QRTZ_JOB_DETAILS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  JOB_NAME  VARCHAR(200) NOT NULL,
  JOB_GROUP VARCHAR(200) NOT NULL,
  DESCRIPTION VARCHAR(250) NULL,
  JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
  IS_DURABLE VARCHAR(1) NOT NULL,
  IS_NONCONCURRENT VARCHAR(1) NOT NULL,
  IS_UPDATE_DATA VARCHAR(1) NOT NULL,
  REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
  JOB_DATA BLOB NULL,
  PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE IF NOT EXISTS QRTZ_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  TRIGGER_NAME VARCHAR(200) NOT NULL,
  TRIGGER_GROUP VARCHAR(200) NOT NULL,
  JOB_NAME  VARCHAR(200) NOT NULL,
  JOB_GROUP VARCHAR(200) NOT NULL,
  DESCRIPTION VARCHAR(250) NULL,
  NEXT_FIRE_TIME BIGINT(13) NULL,
  PREV_FIRE_TIME BIGINT(13) NULL,
  PRIORITY INTEGER NULL,
  TRIGGER_STATE VARCHAR(16) NOT NULL,
  TRIGGER_TYPE VARCHAR(8) NOT NULL,
  START_TIME BIGINT(13) NOT NULL,
  END_TIME BIGINT(13) NULL,
  CALENDAR_NAME VARCHAR(200) NULL,
  MISFIRE_INSTR SMALLINT(2) NULL,
  JOB_DATA BLOB NULL,
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
  REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE IF NOT EXISTS QRTZ_SIMPLE_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  TRIGGER_NAME VARCHAR(200) NOT NULL,
  TRIGGER_GROUP VARCHAR(200) NOT NULL,
  REPEAT_COUNT BIGINT(7) NOT NULL,
  REPEAT_INTERVAL BIGINT(12) NOT NULL,
  TIMES_TRIGGERED BIGINT(10) NOT NULL,
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE IF NOT EXISTS QRTZ_CRON_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  TRIGGER_NAME VARCHAR(200) NOT NULL,
  TRIGGER_GROUP VARCHAR(200) NOT NULL,
  CRON_EXPRESSION VARCHAR(200) NOT NULL,
  TIME_ZONE_ID VARCHAR(80),
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE IF NOT EXISTS QRTZ_SIMPROP_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  TRIGGER_NAME VARCHAR(200) NOT NULL,
  TRIGGER_GROUP VARCHAR(200) NOT NULL,
  STR_PROP_1 VARCHAR(512) NULL,
  STR_PROP_2 VARCHAR(512) NULL,
  STR_PROP_3 VARCHAR(512) NULL,
  INT_PROP_1 INT NULL,
  INT_PROP_2 INT NULL,
  LONG_PROP_1 BIGINT NULL,
  LONG_PROP_2 BIGINT NULL,
  DEC_PROP_1 NUMERIC(13,4) NULL,
  DEC_PROP_2 NUMERIC(13,4) NULL,
  BOOL_PROP_1 VARCHAR(1) NULL,
  BOOL_PROP_2 VARCHAR(1) NULL,
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE IF NOT EXISTS QRTZ_BLOB_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  TRIGGER_NAME VARCHAR(200) NOT NULL,
  TRIGGER_GROUP VARCHAR(200) NOT NULL,
  BLOB_DATA BLOB NULL,
  PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
  FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
  REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE IF NOT EXISTS QRTZ_CALENDARS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  CALENDAR_NAME  VARCHAR(200) NOT NULL,
  CALENDAR BLOB NOT NULL,
  PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE IF NOT EXISTS QRTZ_PAUSED_TRIGGER_GRPS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  TRIGGER_GROUP  VARCHAR(200) NOT NULL,
  PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE IF NOT EXISTS QRTZ_FIRED_TRIGGERS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  ENTRY_ID VARCHAR(95) NOT NULL,
  TRIGGER_NAME VARCHAR(200) NOT NULL,
  TRIGGER_GROUP VARCHAR(200) NOT NULL,
  INSTANCE_NAME VARCHAR(200) NOT NULL,
  FIRED_TIME BIGINT(13) NOT NULL,
  SCHED_TIME BIGINT(13) NOT NULL,
  PRIORITY INTEGER NOT NULL,
  STATE VARCHAR(16) NOT NULL,
  JOB_NAME VARCHAR(200) NULL,
  JOB_GROUP VARCHAR(200) NULL,
  IS_NONCONCURRENT VARCHAR(1) NULL,
  REQUESTS_RECOVERY VARCHAR(1) NULL,
  PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE IF NOT EXISTS QRTZ_SCHEDULER_STATE
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  INSTANCE_NAME VARCHAR(200) NOT NULL,
  LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
  CHECKIN_INTERVAL BIGINT(13) NOT NULL,
  PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE IF NOT EXISTS QRTZ_LOCKS
(
  SCHED_NAME VARCHAR(120) NOT NULL,
  LOCK_NAME  VARCHAR(40) NOT NULL,
  PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);

As we see from the above commands, we create a database named QUARTZ_SCHEMA and several tables in it. These tables will persist the jobs, schedulers and triggers etc required by Quartz. You don’t need to memorize these commands, as they are provided by Quartz.

4. Setup the Quartz properties

In our maven project we add a quartz.properties file under src/main/resources, , which will hold the properties required by Quartz to connect to MySQL:

quartz.properties

# Database properties
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource=quartzDataSource
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.dataSource.quartzDataSource.driver=com.mysql.jdbc.Driver
org.quartz.dataSource.quartzDataSource.URL=jdbc:mysql://localhost:3306/quartz_schema
org.quartz.dataSource.quartzDataSource.user=root
org.quartz.dataSource.quartzDataSource.password=change_me

# Non-database specific properties
org.quartz.scheduler.instanceName=DatabaseScheduler
org.quartz.scheduler.instanceId=EXAMPLE_INSTANCE
org.quartz.scheduler.skipUpdateCheck=true
org.quartz.threadPool.threadCount=1

In the above properties, you should change the org.quartz.dataSource.quartzDataSource.password to the one you configured when you installed MySQL. Let’s see in more details the database properties one by one:

  • org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX – Handles database commits and rollbacks
  • org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate – Abstract class for JDBC drivers to connect to database
  • org.quartz.jobStore.dataSource=quartzDataSource – Random name of datasource
  • org.quartz.jobStore.tablePrefix=QRTZ_ – The prefix of the Quartz tables created
  • org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool – Simple implementation of a thread pool with fixed number of Threads
  • org.quartz.dataSource.quartzDataSource.driver=com.mysql.jdbc.Driver – The MySQL JDBC driver
  • org.quartz.dataSource.quartzDataSource.URL=jdbc:mysql://localhost:3306/quartz_schema – The MySQL IP, port and database name the Quartz tables were created in
  • org.quartz.dataSource.quartzDataSource.user=root– The MySQL user
  • org.quartz.dataSource.quartzDataSource.password=change_me – The MySQL password

5. Run the example

We have finished the configuration of the database and properties to connect to MySQL. In this step, we will implement the code required to run a job and persist the data into MySQL:

SimpleJob.java

public class SimpleJob implements Job {
    
    private final Logger log = LoggerFactory.getLogger(SimpleJob.class);

    public void execute(JobExecutionContext context) throws JobExecutionException {
        log.info("SimpleJob executed!");
    }
}

QuartzExample.java

public class QuartzExample {

    public void run() throws Exception {
        // create the scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();

        // define the job and tie it to the SimpleJob class
        JobDetail job = JobBuilder.newJob(SimpleJob.class)
                .withIdentity("myJob", "myGroup")
                .build();

        // create the trigger and define its schedule to run every 3 seconds
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "myGroup")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withIntervalInSeconds(3)
                        .repeatForever())
                .build();

        // add the job details to the scheduler and associate it with the trigger
        scheduler.scheduleJob(job, trigger);

        // start the scheduler
        scheduler.start();

        // wait long enough to see the job execution
        Thread.sleep(5 * 1000);

        // shutdown the scheduler
        scheduler.shutdown(true);
    }

    public static void main(String[] args) throws Exception {
        // run the example
        QuartzExample example = new QuartzExample();
        example.run();
    }
}

In the example above, we create the SimpleJob job, a very simple job which only outputs a single line of code when it gets executed. In the QuartzExample class, this job is added to the scheduler and set for execution every 3 seconds. Finally the scheduler shuts down after 8 seconds (see Thread.sleep(8 * 1000)). Let’s run main method of QuartzExample class and see the output:

Output

May 15, 2019 7:49:34 AM com.mchange.v2.log.MLog 
INFO: MLog clients using java 1.4+ standard logging.
May 15, 2019 7:49:34 AM com.mchange.v2.c3p0.C3P0Registry banner
INFO: Initializing c3p0-0.9.1.1 [built 15-March-2007 01:32:31; debug? true; trace: 10]
07:49:34.461 [main] INFO org.quartz.impl.StdSchedulerFactory - Using default implementation for ThreadExecutor
07:49:34.526 [main] INFO org.quartz.core.SchedulerSignalerImpl - Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
07:49:34.526 [main] INFO org.quartz.core.QuartzScheduler - Quartz Scheduler v.2.2.1 created.
07:49:34.527 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Using thread monitor-based data access locking (synchronization).
07:49:34.528 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - JobStoreTX initialized.
07:49:34.529 [main] INFO org.quartz.core.QuartzScheduler - Scheduler meta-data: Quartz Scheduler (v2.2.1) 'DatabaseScheduler' with instanceId 'EXAMPLE_INSTANCE'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 1 threads.
  Using job-store 'org.quartz.impl.jdbcjobstore.JobStoreTX' - which supports persistence. and is not clustered.

07:49:34.529 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler 'DatabaseScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
07:49:34.529 [main] INFO org.quartz.impl.StdSchedulerFactory - Quartz scheduler version: 2.2.1
07:49:34.535 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is desired by: main
07:49:34.536 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is being obtained: main
07:49:34.536 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' given to: main
May 15, 2019 7:49:34 AM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> z8kflta21fk59q91urzcyj|5ccd43c2, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kflta21fk59q91urzcyj|5ccd43c2, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/QUARTZ_SCHEMA, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 0, maxStatementsPerConnection -> 120, minPoolSize -> 1, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
07:49:34.990 [main] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - Adding TriggerPersistenceDelegate of type: org.quartz.impl.jdbcjobstore.SimpleTriggerPersistenceDelegate
07:49:34.991 [main] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - Adding TriggerPersistenceDelegate of type: org.quartz.impl.jdbcjobstore.CronTriggerPersistenceDelegate
07:49:34.996 [main] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - Adding TriggerPersistenceDelegate of type: org.quartz.impl.jdbcjobstore.CalendarIntervalTriggerPersistenceDelegate
07:49:35.000 [main] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - Adding TriggerPersistenceDelegate of type: org.quartz.impl.jdbcjobstore.DailyTimeIntervalTriggerPersistenceDelegate
07:49:35.081 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' retuned by: main
07:49:35.083 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is desired by: main
07:49:35.083 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is being obtained: main
07:49:35.083 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' given to: main
07:49:35.095 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Freed 0 triggers from 'acquired' / 'blocked' state.
07:49:35.101 [main] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - Found 0 triggers that missed their scheduled fire-time.
07:49:35.106 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovering 0 jobs that were in-progress at the time of the last shut-down.
07:49:35.106 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Recovery complete.
07:49:35.107 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 'complete' triggers.
07:49:35.109 [main] INFO org.quartz.impl.jdbcjobstore.JobStoreTX - Removed 0 stale fired job entries.
07:49:35.110 [main] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' retuned by: main
07:49:35.111 [main] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - JobStore background threads started (as scheduler was started).
07:49:35.111 [QuartzScheduler_DatabaseScheduler-EXAMPLE_INSTANCE_MisfireHandler] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - MisfireHandler: scanning for misfires...
07:49:35.111 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DatabaseScheduler_$_EXAMPLE_INSTANCE started.
07:49:35.114 [QuartzScheduler_DatabaseScheduler-EXAMPLE_INSTANCE_MisfireHandler] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - Found 0 triggers that missed their scheduled fire-time.
07:49:35.146 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
07:49:35.148 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is desired by: DatabaseScheduler_QuartzSchedulerThread
07:49:35.148 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is being obtained: DatabaseScheduler_QuartzSchedulerThread
07:49:35.148 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' given to: DatabaseScheduler_QuartzSchedulerThread
07:49:35.172 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' retuned by: DatabaseScheduler_QuartzSchedulerThread
07:49:35.177 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'myGroup.myJob', class=com.javacodegeeks.quartz.builders.SimpleJob
07:49:35.179 [DatabaseScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job myGroup.myJob
07:49:35.180 [DatabaseScheduler_Worker-1] INFO com.javacodegeeks.quartz.builders.SimpleJob - SimpleJob executed!
07:49:35.181 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is desired by: DatabaseScheduler_Worker-1
07:49:35.181 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is being obtained: DatabaseScheduler_Worker-1
07:49:35.181 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' given to: DatabaseScheduler_Worker-1
07:49:35.196 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' retuned by: DatabaseScheduler_Worker-1
07:49:35.217 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
07:49:37.534 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is desired by: DatabaseScheduler_QuartzSchedulerThread
07:49:37.534 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is being obtained: DatabaseScheduler_QuartzSchedulerThread
07:49:37.535 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' given to: DatabaseScheduler_QuartzSchedulerThread
07:49:37.555 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' retuned by: DatabaseScheduler_QuartzSchedulerThread
07:49:37.556 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.simpl.PropertySettingJobFactory - Producing instance of Job 'myGroup.myJob', class=com.javacodegeeks.quartz.builders.SimpleJob
07:49:37.556 [DatabaseScheduler_Worker-1] DEBUG org.quartz.core.JobRunShell - Calling execute on job myGroup.myJob
07:49:37.556 [DatabaseScheduler_Worker-1] INFO com.javacodegeeks.quartz.builders.SimpleJob - SimpleJob executed!
07:49:37.556 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is desired by: DatabaseScheduler_Worker-1
07:49:37.556 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is being obtained: DatabaseScheduler_Worker-1
07:49:37.556 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' given to: DatabaseScheduler_Worker-1
07:49:37.561 [DatabaseScheduler_Worker-1] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' retuned by: DatabaseScheduler_Worker-1
07:49:37.581 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 1 triggers
07:49:40.115 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DatabaseScheduler_$_EXAMPLE_INSTANCE shutting down.
07:49:40.116 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DatabaseScheduler_$_EXAMPLE_INSTANCE paused.
07:49:40.116 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is desired by: DatabaseScheduler_QuartzSchedulerThread
07:49:40.116 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' is being obtained: DatabaseScheduler_QuartzSchedulerThread
07:49:40.116 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' given to: DatabaseScheduler_QuartzSchedulerThread
07:49:40.132 [DatabaseScheduler_QuartzSchedulerThread] DEBUG org.quartz.impl.jdbcjobstore.SimpleSemaphore - Lock 'TRIGGER_ACCESS' retuned by: DatabaseScheduler_QuartzSchedulerThread
07:49:40.133 [main] DEBUG org.quartz.simpl.SimpleThreadPool - Shutting down threadpool...
07:49:40.578 [DatabaseScheduler_Worker-1] DEBUG org.quartz.simpl.SimpleThreadPool - WorkerThread is shut down.
07:49:40.579 [main] DEBUG org.quartz.simpl.SimpleThreadPool - No executing jobs remaining, all threads stopped.
07:49:40.579 [main] DEBUG org.quartz.simpl.SimpleThreadPool - Shutdown of threadpool complete.
07:49:40.581 [main] DEBUG org.quartz.impl.jdbcjobstore.JobStoreTX - JobStore background threads shutdown.
07:49:40.581 [main] INFO org.quartz.core.QuartzScheduler - Scheduler DatabaseScheduler_$_EXAMPLE_INSTANCE shutdown complete.

From the above output, we see that the job ran 2 times in total, at an interval of 3 seconds, before the scheduler was manually shut down by the main thread. We see in line 24 that Quartz manages to connect to the MySQL database we configured. In the next section, we will examine what data is saved into the database tables we created.

6. Examine the database

In the previous section, we scheduled a very simple job using a trigger that runs at an interval of 3 seconds. Let’s see what data is saved in MySQL when the job gets executed:

Queries

SELECT * FROM quartz_schema.qrtz_job_details;
+-------------------+----------+-----------+-------------+-----------------------------+------------+------------------+----------------+-------------------+----------------+
| SCHED_NAME        | JOB_NAME | JOB_GROUP | DESCRIPTION | JOB_CLASS_NAME              | IS_DURABLE | IS_NONCONCURRENT | IS_UPDATE_DATA | REQUESTS_RECOVERY | JOB_DATA       |
+-------------------+----------+-----------+-------------+-----------------------------+------------+------------------+----------------+-------------------+----------------+
| DatabaseScheduler | myJob    | myGroup   | NULL        | com.javacodegeeks.SimpleJob | 0          | 0                | 0              | 0                 | (Binary/Image) |
+-------------------+----------+-----------+-------------+-----------------------------+------------+------------------+----------------+-------------------+----------------+
1 row in set (0.00 sec)

SELECT * FROM quartz_schema.qrtz_simple_triggers;
+-------------------+--------------+---------------+--------------+-----------------+-----------------+
| SCHED_NAME        | TRIGGER_NAME | TRIGGER_GROUP | REPEAT_COUNT | REPEAT_INTERVAL | TIMES_TRIGGERED |
+-------------------+--------------+---------------+--------------+-----------------+-----------------+
| DatabaseScheduler | myTrigger    | myGroup       |           -1 |            3000 |               2 |
+-------------------+--------------+---------------+--------------+-----------------+-----------------+
1 row in set (0.00 sec)

SELECT * FROM quartz_schema.qrtz_triggers;
+-------------------+--------------+---------------+----------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+
| SCHED_NAME        | TRIGGER_NAME | TRIGGER_GROUP | JOB_NAME | JOB_GROUP | DESCRIPTION | NEXT_FIRE_TIME | PREV_FIRE_TIME | PRIORITY | TRIGGER_STATE | TRIGGER_TYPE | START_TIME    | END_TIME | CALENDAR_NAME | MISFIRE_INSTR | JOB_DATA |
+-------------------+--------------+---------------+----------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+
| DatabaseScheduler | myTrigger    | myGroup       | myJob    | myGroup   | NULL        |  1557895780532 |  1557895777532 |        5 | WAITING       | SIMPLE       | 1557895774532 |        0 | NULL          |             0 |          |
+-------------------+--------------+---------------+----------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+
1 row in set (0.00 sec)

As we see from the output of the 3 queries we executed above, the job data (name, group, Java class) is saved in the quartz_schema.qrtz_job_details table and the trigger data (name, group, interval) is saved in the quartz_schema.qrtz_simple_triggers table. The most important data, which is the job execution data, is saved in the quartz_schema.qrtz_triggers table. In this table, some job and trigger data is also saved, but we also find the last and next execution time of the job. This data is very important for Quartz to schedule the job.

7. Conclusion

In this article, we setup a new MySQL database and tables in which we saved the jobs and triggers of Quartz. We went through a code example using a simple project which schedules a job and saves the Quartz data into MySQL. Finally, we examined the data saved by Quartz into the database.

8. Download the Eclipse project

Download
You can download the full source code of the above example here: Java Quartz with MySQL Example

Lefteris Karageorgiou

Lefteris is a Lead Software Engineer at ZuluTrade and has been responsible for re-architecting the backend of the main website from a monolith to event-driven microservices using Java, Spring Boot/Cloud, RabbitMQ, Redis. He has extensive work experience for over 10 years in Software Development, working mainly in the FinTech and Sports Betting industries. Prior to joining ZuluTrade, Lefteris worked as a Senior Java Developer at Inspired Gaming Group in London, building enterprise sports betting applications for William Hills and Paddy Power. He enjoys working with large-scalable, real-time and high-volume systems deployed into AWS and wants to combine his passion for technology and traveling by attending software conferences all over the world.
Subscribe
Notify of
guest

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

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Rahul
Rahul
3 years ago

Hi,
 
I have a multi-server environment and there are some internal jobs which are to be executed on timely basis. Now, as it is multi-server environment I am afraid that a single scheduled job will get triggered at the same time on each server i.e. if I have 3 instances of my servers running then each server will trigger same job at the scheduled time.
 
Could you please suggest a way to tackle this.
 
Thanks in advance !

Bhupesh dhaundiyal
Bhupesh dhaundiyal
3 years ago
Reply to  Rahul

Hello Rahul,

Quartz maintain lock to avoid multiple executions of same job in multi instance env.

Back to top button