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
You can download the full source code of the above example here: Java Quartz with MySQL Example
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 !
Hello Rahul,
Quartz maintain lock to avoid multiple executions of same job in multi instance env.