- Main Configuration (configuration of primary scheduler settings, transactions)
- Configuration of ThreadPool (tune resources for job execution)
- Configuration of Listeners (your application can receive notification of scheduled events)
- Configuration of Plug-Ins (add functionality to your scheduler)
- Configuration of RMI Server and Client (use a Quartz instance from a remote process)
- Configuration of RAMJobStore (store jobs and triggers in memory)
- Configuration of JDBC-JobStoreTX (store jobs and triggers in a database via JDBC)
- Configuration of JDBC-JobStoreCMT (JDBC with JTA container-managed transactions)
- Configuration of DataSources (for use by the JDBC-JobStores)
- Configuration of Database Clustering (achieve fail-over and load-balancing with JDBC-JobStore)
- Configuration of
TerracottaJobStore
(Clustering without a database!)
Configuration of Quartz is typically done through the use of a properties file, in conjunction with the use of StdSchedulerFactory
(which consumes the configuration file and instantiates a scheduler).
By default, StdSchedulerFactory
load a properties file named quartz.properties
from the "current working directory". If that fails, then the quartz.properties
file located (as a resource) in the org/quartz
package is loaded. If you wish to use a file other than these defaults, you must define the system property org.quartz.properties
to point to the file you want.
Alternatively, you can explicitly initialize the factory by calling one of the initialize(xx)
methods before calling getScheduler()
on the StdSchedulerFactory
.
Instances of the specified JobStore, ThreadPool, and other SPI classes will be created by name, and then any additional properties specified for them in the config file will be set on the instance by calling an equivalent set
method. For example if the properties file contains the property org.quartz.jobStore.myProp = 10
then after the JobStore class has been instantiated, the method setMyProp()
will be called on it. Type conversion to primitive Java types (int, long, float, double, boolean, and String) are performed before calling the property’s setter method.
One property can reference another property’s value by specifying a value following the convention of [email protected]
, for example, to reference the scheduler’s instance name as the value for some other property, you would use [email protected]
.
The properties for configuring various aspect of a scheduler are described in these sub-documents: Choose a topic:
Configure Main Scheduler Settings
These properties configure the identification of the scheduler, and various other "top level" settings.
Property Name | Req'd | Type | Default Value |
---|---|---|---|
org.quartz.scheduler.instanceName | no | string | 'QuartzScheduler' |
org.quartz.scheduler.instanceId | no | string | 'NON_CLUSTERED' |
org.quartz.scheduler.instanceIdGenerator.class | no | string (class name) | org.quartz.simpl .SimpleInstanceIdGenerator |
org.quartz.scheduler.threadName | no | string | instanceName + '_QuartzSchedulerThread' |
org.quartz.scheduler .makeSchedulerThreadDaemon |
no | boolean | false |
org.quartz.scheduler .threadsInheritContextClassLoaderOfInitializer |
no | boolean | false |
org.quartz.scheduler.idleWaitTime | no | long | 30000 |
org.quartz.scheduler.dbFailureRetryInterval | no | long | 15000 |
org.quartz.scheduler.classLoadHelper.class | no | string (class name) | org.quartz.simpl .CascadingClassLoadHelper |
org.quartz.scheduler.jobFactory.class | no | string (class name) | org.quartz.simpl.PropertySettingJobFactory |
org.quartz.context.key.SOME_KEY | no | string | none |
org.quartz.scheduler.userTransactionURL | no | string (url) | 'java:comp/UserTransaction' |
org.quartz.scheduler .wrapJobExecutionInUserTransaction |
no | boolean | false |
org.quartz.scheduler.skipUpdateCheck | no | boolean | false |
org.quartz.scheduler .batchTriggerAcquisitionMaxCount |
no | int | 1 |
org.quartz.scheduler .batchTriggerAcquisitionFireAheadTimeWindow |
no | long | 0 |
org.quartz.scheduler.instanceName
Can be any string, and the value has no meaning to the scheduler itself - but rather serves as a mechanism for client code to distinguish schedulers when multiple instances are used within the same program. If you are using the clustering features, you must use the same name for every instance in the cluster that is 'logically' the same Scheduler.
org.quartz.scheduler.instanceId
Can be any string, but must be unique for all schedulers working as if they are the same 'logical' Scheduler within a cluster. You may use the value "AUTO" as the instanceId if you wish the Id to be generated for you. Or the value "SYS_PROP" if you want the value to come from the system property "org.quartz.scheduler.instanceId".
org.quartz.scheduler.instanceIdGenerator.class
Only used if org.quartz.scheduler.instanceId is set to "AUTO". Defaults to "org.quartz.simpl.SimpleInstanceIdGenerator", which generates an instance id based upon host name and time stamp. Other IntanceIdGenerator implementations include SystemPropertyInstanceIdGenerator (which gets the instance id from the system property "org.quartz.scheduler.instanceId", and HostnameInstanceIdGenerator which uses the local host name (InetAddress.getLocalHost().getHostName()). You can also implement the InstanceIdGenerator interface your self.
org.quartz.scheduler.threadName
Can be any String that is a valid name for a java thread. If this property is not specified, the thread will receive the scheduler’s name ("org.quartz.scheduler.instanceName") plus an the appended string '_QuartzSchedulerThread'.
org.quartz.scheduler.makeSchedulerThreadDaemon
A boolean value ('true' or 'false') that specifies whether the main thread of the scheduler should be a daemon thread or
not. See also the org.quartz.scheduler.makeSchedulerThreadDaemon property for tuning the SimpleThreadPool
if that is the thread pool implementation you are using (which is most likely the case).
org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer
A boolean value ('true' or 'false') that specifies whether the threads spawned by Quartz will inherit the context ClassLoader of the initializing thread (thread that initializes the Quartz instance). This will affect Quartz main scheduling thread, JDBCJobStore’s misfire handling thread (if JDBCJobStore is used), cluster recovery thread (if clustering is used), and threads in SimpleThreadPool (if SimpleThreadPool is used). Setting this value to 'true' may help with class loading, JNDI look-ups, and other issues related to using Quartz within an application server.
org.quartz.scheduler.idleWaitTime
Is the amount of time in milliseconds that the scheduler will wait before re-queries for available triggers when the scheduler is otherwise idle. Normally you should not have to 'tune' this parameter, unless you’re using XA transactions, and are having problems with delayed firings of triggers that should fire immediately. Values less than 5000 ms are not recommended as it will cause excessive database querying. Values less than 1000 are not legal.
org.quartz.scheduler.dbFailureRetryInterval
Is the amount of time in milliseconds that the scheduler will wait between re-tries when it has detected a loss of connectivity within the JobStore (e.g. to the database). This parameter is obviously not very meaningful when using RamJobStore.
org.quartz.scheduler.classLoadHelper.class
Defaults to the most robust approach, which is to use the "org.quartz.simpl.CascadingClassLoadHelper" class - which in turn uses every other ClassLoadHelper class until one works. You should probably not find the need to specify any other class for this property, though strange things seem to happen within application servers. All of the current possible ClassLoadHelper implementation can be found in the org.quartz.simpl package.
org.quartz.scheduler.jobFactory.class
The class name of the JobFactory to use. A JobFatcory is responsible for producing instances of JobClasses. The default is 'org.quartz.simpl.PropertySettingJobFactory', which simply calls newInstance() on the class to produce a new instance each time execution is about to occur. PropertySettingJobFactory also reflectively sets the job’s bean properties using the contents of the SchedulerContext and Job and Trigger JobDataMaps.
org.quartz.context.key.SOME_KEY
Represent a name-value pair that will be placed into the "scheduler context" as strings. (see Scheduler.getContext()). So for example, the setting "org.quartz.context.key.MyKey = MyValue" would perform the equivalent of scheduler.getContext().put("MyKey", "MyValue").
NOTE: The Transaction-Related properties should be left out of the config file unless you are using JTA transactions.
org.quartz.scheduler.userTransactionURL
Should be set to the JNDI URL at which Quartz can locate the Application Server’s UserTransaction manager. The default value (if not specified) is "java:comp/UserTransaction" - which works for almost all Application Servers. Websphere users may need to set this property to "jta/usertransaction". This is only used if Quartz is configured to use JobStoreCMT, and org.quartz.scheduler.wrapJobExecutionInUserTransaction is set to true.
org.quartz.scheduler.wrapJobExecutionInUserTransaction
Should be set to "true" if you want Quartz to start a UserTransaction before calling execute on your job. The Tx will commit after the job’s execute method completes, and after the JobDataMap is updated (if it is a StatefulJob). The default value is "false". You may also be interested in using the @ExecuteInJTATransaction annotation on your job class, which lets you control for an individual job whether Quartz should start a JTA transaction - whereas this property causes it to occur for all jobs.
org.quartz.scheduler.skipUpdateCheck
Whether or not to skip running a quick web request to determine if there is an updated version of Quartz available for download. If the check runs, and an update is found, it will be reported as available in Quartz’s logs. You can also disable the update check with the system property "org.terracotta.quartz.skipUpdateCheck=true" (which you can set in your system environment or as a -D on the java command line). It is recommended that you disable the update check for production deployments.
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. The larger the number, the more efficient firing is (in situations where there are very many triggers needing to be fired all at once) - but at the cost of possible imbalanced load between cluster nodes. If the value of this property is set to > 1, and JDBC JobStore is used, then the property "org.quartz.jobStore.acquireTriggersWithinLock" must be set to "true" to avoid data corruption.
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. The larger the number, the more likely batch acquisition of triggers to fire will be able to select and fire more than 1 trigger at a time - at the cost of trigger schedule not being honored precisely (triggers may fire this amount early). This may be useful (for performance’s sake) in situations where the scheduler has very large numbers of triggers that need to be fired at or near the same time.
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.threadPool.class | yes | string (class name) | null |
org.quartz.threadPool.threadCount | yes | int | -1 |
org.quartz.threadPool.threadPriority | no | int | Thread.NORM_PRIORITY (5) |
org.quartz.threadPool.class
Is the name of the ThreadPool implementation you wish to use. The threadpool that ships with Quartz is "org.quartz.simpl.SimpleThreadPool", and should meet the needs of nearly every user. It has very simple behavior and is very well tested. It provides a fixed-size pool of threads that 'live' the lifetime of the Scheduler.
org.quartz.threadPool.threadCount
Can be any positive integer, although you should realize that only numbers between 1 and 100 are very practical. This is the number of threads that are available for concurrent execution of jobs. If you only have a few jobs that fire a few times a day, then 1 thread is plenty! If you have tens of thousands of jobs, with many firing every minute, then you probably want a thread count more like 50 or 100 (this highly depends on the nature of the work that your jobs perform, and your systems resources!).
org.quartz.threadPool.threadPriority
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).
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.threadPool.makeThreadsDaemons | no | boolean | false |
org.quartz.threadPool.threadsInheritGroupOfInitializingThread | no | boolean | true |
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread | no | boolean | false |
org.quartz.threadPool.threadNamePrefix | no | string | [Scheduler Name]_Worker |
org.quartz.threadPool.makeThreadsDaemons
Can be set to "true" to have the threads in the pool created as daemon threads. Default is "false". See also the ConfigMain org.quartz.scheduler.makeSchedulerThreadDaemon
property.
org.quartz.threadPool.threadsInheritGroupOfInitializingThread
Can be "true" or "false", and defaults to true.
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread
Can be "true" or "false", and defaults to false.
org.quartz.threadPool.threadNamePrefix
The prefix for thread names in the worker pool - will be postpended with a number.
If you use your own implementation of a thread pool, you can have properties set on it reflectively simply by naming the property as thus:
Setting Properties on a Custom ThreadPool
org.quartz.threadPool.class = com.mycompany.goo.FooThreadPool org.quartz.threadPool.somePropOfFooThreadPool = someValue
Global listeners can be instantiated and configured by StdSchedulerFactory
, or your application can do it itself
at runtime, and then register the listeners with the scheduler. "Global" listeners listen to the events of every
job/trigger rather than just the jobs/triggers that directly reference them.
Configuring listeners through the configuration file consists of giving then a name, and then specifying the class name, and any other properties to be set on the instance. The class must have a no-arg constructor, and the properties are set reflectively. Only primitive data type values (including Strings) are supported.
Thus, the general pattern for defining a "global" TriggerListener is:
Configuring a Global TriggerListener
org.quartz.triggerListener.NAME.class = com.foo.MyListenerClass org.quartz.triggerListener.NAME.propName = propValue org.quartz.triggerListener.NAME.prop2Name = prop2Value
And the general pattern for defining a "global" JobListener is:
Configuring a Global JobListener
org.quartz.jobListener.NAME.class = com.foo.MyListenerClass org.quartz.jobListener.NAME.propName = propValue org.quartz.jobListener.NAME.prop2Name = prop2Value
Like listeners configuring plugins through the configuration file consists of giving then a name, and then specifying the class name, and any other properties to be set on the instance. The class must have a no-arg constructor, and the properties are set reflectively. Only primitive data type values (including Strings) are supported.
Thus, the general pattern for defining a plug-in is:
Configuring a Plugin
org.quartz.plugin.NAME.class = com.foo.MyPluginClass org.quartz.plugin.NAME.propName = propValue org.quartz.plugin.NAME.prop2Name = prop2Value
There are several Plugins that come with Quartz, that can be found in the org.quartz.plugins package (and subpackages). Example of configuring a few of them are as follows:
The logging trigger history plugin catches trigger events (it is also a trigger listener) and logs then with Jakarta Commons-Logging. See the class’s JavaDoc for a list of all the possible parameters.
Sample configuration of Logging Trigger History Plugin
org.quartz.plugin.triggHistory.class = \ org.quartz.plugins.history.LoggingTriggerHistoryPlugin org.quartz.plugin.triggHistory.triggerFiredMessage = \ Trigger \{1\}.\{0\} fired job \{6\}.\{5\} at: \{4, date, HH:mm:ss MM/dd/yyyy} org.quartz.plugin.triggHistory.triggerCompleteMessage = \ Trigger \{1\}.\{0\} completed firing job \{6\}.\{5\} at \{4, date, HH:mm:ss MM/dd/yyyy\}.
Job initialization plugin reads a set of jobs and triggers from an XML file, and adds them to the scheduler during initialization. It can also delete exiting data. See the class’s JavaDoc for more details.
Sample configuration of JobInitializationPlugin
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin org.quartz.plugin.jobInitializer.fileNames = data/my_job_data.xml org.quartz.plugin.jobInitializer.failOnFileNotFound = true
The XML schema definition for the file can be found here:
The shutdown-hook plugin catches the event of the JVM terminating, and calls shutdown on the scheduler.
Sample configuration of ShutdownHookPlugin
org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin org.quartz.plugin.shutdownhook.cleanShutdown = true
None of the primary properties are required, and all have 'reasonable' defaults. When using Quartz via RMI, you need to start an instance of Quartz with it configured to "export" its services via RMI. You then create clients to the server by configuring a Quartz scheduler to "proxy" its work to the server.
NOTE: Some users experience problems with class availability (namely Job classes) between the client and server. To work through these problems you’ll need an understanding of RMI’s "codebase" and RMI security managers. You may find these resources to be useful:
An excellent description of RMI and codebase: http://www.kedwards.com/jini/codebase.html. One of the important points is to realize that "codebase" is used by the client!
Quick info about security managers: http://gethelp.devx.com/techtips/java_pro/10MinuteSolutions/10min0500.asp
And finally from the Java API docs, read the docs for the RMISecurityManager.
Property Name | Required | Default Value |
---|---|---|
org.quartz.scheduler.rmi.export | no | false |
org.quartz.scheduler.rmi.registryHost | no | 'localhost' |
org.quartz.scheduler.rmi.registryPort | no | 1099 |
org.quartz.scheduler.rmi.createRegistry | no | 'never' |
org.quartz.scheduler.rmi.serverPort | no | random |
org.quartz.scheduler.rmi.proxy | no | false |
org.quartz.scheduler.rmi.export
If you want the Quartz Scheduler to export itself via RMI as a server then set the 'rmi.export' flag to true.
org.quartz.scheduler.rmi.registryHost
The host at which the RMI Registry can be found (often 'localhost').
org.quartz.scheduler.rmi.registryPort
The port on which the RMI Registry is listening (usually 1099).
org.quartz.scheduler.rmi.createRegistry
Set the 'rmi.createRegistry' flag according to how you want Quartz to cause the creation of an RMI Registry. Use "false" or "never" if you don’t want Quartz to create a registry (e.g. if you already have an external registry running). Use "true" or "as_needed" if you want Quartz to first attempt to use an existing registry, and then fall back to creating one. Use "always" if you want Quartz to attempt creating a Registry, and then fall back to using an existing one. If a registry is created, it will be bound to port number in the given 'org.quartz.scheduler.rmi.registryPort' property, and 'org.quartz.rmi.registryHost' should be "localhost".
org.quartz.scheduler.rmi.serverPort
The port on which the the Quartz Scheduler service will bind and listen for connections. By default, the RMI service will 'randomly' select a port as the scheduler is bound to the RMI Registry.
org.quartz.scheduler.rmi.proxy
If you want to connect to (use) a remotely served scheduler, then set the 'org.quartz.scheduler.rmi.proxy' flag to true. You must also then specify a host and port for the RMI Registry process - which is typically 'localhost' port 1099.
NOTE: It does not make sense to specify a 'true' value for both 'org.quartz.scheduler.rmi.export' and 'org.quartz.scheduler.rmi.proxy' in the same config file - if you do, the 'export' option will be ignored. A value of 'false' for both 'export' and 'proxy' properties is of course valid, if you’re not using Quartz via RMI.
RAMJobStore is used to store scheduling information (job, triggers and calendars) within memory. RAMJobStore is fast and lightweight, but all scheduling information is lost when the process terminates.
RAMJobStore
is selected by setting the org.quartz.jobStore.class
property as such:
Setting The Scheduler’s JobStore
to RAMJobStore
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
RAMJobStore can be tuned with the following properties:
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.jobStore.misfireThreshold | no | int | 60000 |
org.quartz.jobStore.misfireThreshold
The the number of milliseconds the scheduler will 'tolerate' a trigger to pass its next-fire-time by, before being considered "misfired". The default value (if you don’t make an entry of this property in your configuration) is 60000 (60 seconds).
JDBCJobStore is used to store scheduling information (job, triggers and calendars) within a relational database. There are actually two seperate JDBCJobStore classes that you can select between, depending on the transactional behaviour you need.
JobStoreTX manages all transactions itself by calling commit() (or rollback()) on the database connection after every action (such as the addition of a job). JDBCJobStore is appropriate if you are using Quartz in a stand-alone application, or within a servlet container if the application is not using JTA transactions.
The JobStoreTX is selected by setting the org.quartz.jobStore.class
property as such:
Setting The Scheduler’s JobStore
to JobStoreTX
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
JobStoreTX
can be tuned with the following properties:
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.jobStore.driverDelegateClass | yes | string | null |
org.quartz.jobStore.dataSource | yes | string | null |
org.quartz.jobStore.tablePrefix | no | string | "QRTZ_" |
org.quartz.jobStore.useProperties | no | boolean | false |
org.quartz.jobStore.misfireThreshold | no | int | 60000 |
org.quartz.jobStore.isClustered | no | boolean | false |
org.quartz.jobStore.clusterCheckinInterval | no | long | 15000 |
org.quartz.jobStore.maxMisfiresToHandleAtATime | no | int | 20 |
org.quartz.jobStore.dontSetAutoCommitFalse | no | boolean | false |
org.quartz.jobStore.selectWithLockSQL | no | string | "SELECT * FROM {0}LOCKS WHERE SCHED_NAME = {1} AND LOCK_NAME = ? FOR UPDATE" |
org.quartz.jobStore.txIsolationLevelSerializable | no | boolean | false |
org.quartz.jobStore.acquireTriggersWithinLock | no | boolean | false (or true - see doc below) |
org.quartz.jobStore.lockHandler.class | no | string | null |
org.quartz.jobStore.driverDelegateInitString | no | string | null |
org.quartz.jobStore.driverDelegateClass
Driver delegates understand the particular 'dialects' of varies database systems. Possible choices include:
-
org.quartz.impl.jdbcjobstore.StdJDBCDelegate
(for fully JDBC-compliant drivers) -
org.quartz.impl.jdbcjobstore.MSSQLDelegate
(for Microsoft SQL Server, and Sybase) -
org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-
org.quartz.impl.jdbcjobstore.WebLogicDelegate
(for WebLogic drivers) -
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
-
org.quartz.impl.jdbcjobstore.oracle.WebLogicOracleDelegate
(for Oracle drivers used within Weblogic) -
org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate
(for Oracle drivers used within Weblogic) -
org.quartz.impl.jdbcjobstore.CloudscapeDelegate
-
org.quartz.impl.jdbcjobstore.DB2v6Delegate
-
org.quartz.impl.jdbcjobstore.DB2v7Delegate
-
org.quartz.impl.jdbcjobstore.DB2v8Delegate
-
org.quartz.impl.jdbcjobstore.HSQLDBDelegate
-
org.quartz.impl.jdbcjobstore.PointbaseDelegate
-
org.quartz.impl.jdbcjobstore.SybaseDelegate
Note that many databases are known to work with the StdJDBCDelegate, while others are known to work with delegates for other databases, for example Derby works well with the Cloudscape delegate (no surprise there).
org.quartz.jobStore.dataSource
The value of this property must be the name of one the DataSources defined in the configuration properties file. See the ConfigDataSources configuration docs for DataSources for more information.
org.quartz.jobStore.tablePrefix
JDBCJobStore’s "table prefix" property is a string equal to the prefix given to Quartz’s tables that were created in your database. You can have multiple sets of Quartz’s tables within the same database if they use different table prefixes.
org.quartz.jobStore.useProperties
The "use properties" flag instructs JDBCJobStore that all values in JobDataMaps will be Strings, and therefore can be stored as name-value pairs, rather than storing more complex objects in their serialized form in the BLOB column. This is can be handy, as you avoid the class versioning issues that can arise from serializing your non-String classes into a BLOB.
org.quartz.jobStore.misfireThreshold
The the number of milliseconds the scheduler will 'tolerate' a trigger to pass its next-fire-time by, before being considered "misfired". The default value (if you don’t make an entry of this property in your configuration) is 60000 (60 seconds).
org.quartz.jobStore.isClustered
Set to "true" in order to turn on clustering features. This property must be set to "true" if you are having multiple instances of Quartz use the same set of database tables… otherwise you will experience havoc. See the configuration docs for clustering for more information.
org.quartz.jobStore.clusterCheckinInterval
Set the frequency (in milliseconds) at which this instance "checks-in"* with the other instances of the cluster. Affects the quickness of detecting failed instances.
org.quartz.jobStore.maxMisfiresToHandleAtATime
The maximum number of misfired triggers the jobstore will handle in a given pass. Handling many (more than a couple dozen) at once can cause the database tables to be locked long enough that the performance of firing other (not yet misfired) triggers may be hampered.
org.quartz.jobStore.dontSetAutoCommitFalse
Setting this parameter to "true" tells Quartz not to call setAutoCommit(false) on connections obtained from the DataSource(s). This can be helpful in a few situations, such as if you have a driver that complains if it is called when it is already off. This property defaults to false, because most drivers require that setAutoCommit(false) is called.
org.quartz.jobStore.selectWithLockSQL
Must be a SQL string that selects a row in the "LOCKS" table and places a lock on the row. If not set, the default is "SELECT * FROM {0}LOCKS WHERE SCHED_NAME = {1} AND LOCK_NAME = ? FOR UPDATE", which works for most databases. The "{0}" is replaced during run-time with the TABLE_PREFIX that you configured above. The "{1}" is replaced with the scheduler’s name.
org.quartz.jobStore.txIsolationLevelSerializable
A value of "true" tells Quartz (when using JobStoreTX or CMT) to call setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) on JDBC connections. This can be helpful to prevent lock timeouts with some databases under high load, and "long-lasting" transactions.
org.quartz.jobStore.acquireTriggersWithinLock
Whether or not the acquisition of next triggers to fire should occur within an explicit database lock. This was once necessary (in previous versions of Quartz) to avoid dead-locks with particular databases, but is no longer considered necessary, hence the default value is "false".
If "org.quartz.scheduler.batchTriggerAcquisitionMaxCount" is set to > 1, and JDBC JobStore is used, then this property must be set to "true" to avoid data corruption (as of Quartz 2.1.1 "true" is now the default if batchTriggerAcquisitionMaxCount is set > 1).
org.quartz.jobStore.lockHandler.class
The class name to be used to produce an instance of a org.quartz.impl.jdbcjobstore.Semaphore
to be used for locking control on the job store data. This is an advanced configuration feature, which should not be used by most users. By default, Quartz will select the most appropriate (pre-bundled) Semaphore implementation to use. org.quartz.impl.jdbcjobstore.UpdateLockRowSemaphore
QUARTZ-497 may be of interest to MS SQL Server users. See QUARTZ-441.
If you explicitly choose to use this DB Semaphore, you can customize it further on how frequent to poll for DB locks.
Example of Using a Custom StdRowLockSemaphore
Implementation
org.quartz.jobStore.lockHandler.class = org.quartz.impl.jdbcjobstore.StdRowLockSemaphore org.quartz.jobStore.lockHandler.maxRetry = 7 # Default is 3 org.quartz.jobStore.lockHandler.maxRetry = 3000 # Default is 1000 millis
org.quartz.jobStore.driverDelegateInitString
A pipe-delimited list of properties (and their values) that can be passed to the DriverDelegate during initialization time.
The format of the string is as such:
settingName=settingValue|otherSettingName=otherSettingValue|...
The StdJDBCDelegate and all of its descendants (all delegates that ship with Quartz) support a property called 'triggerPersistenceDelegateClasses' which can be set to a comma-separated list of classes that implement the TriggerPersistenceDelegate interface for storing custom trigger types. See the Java classes SimplePropertiesTriggerPersistenceDelegateSupport and SimplePropertiesTriggerPersistenceDelegateSupport for examples of writing a persistence delegate for a custom trigger.
JDBCJobStore is used to store scheduling information (job, triggers and calendars) within a relational database. There are actually two separate JDBCJobStore classes that you can select between, depending on the transactional behaviour you need.
JobStoreCMT relies upon transactions being managed by the application which is using Quartz. A JTA transaction must be in progress before attempt to schedule (or unschedule) jobs/triggers. This allows the "work" of scheduling to be part of the applications "larger" transaction. JobStoreCMT actually requires the use of two datasources - one that has it’s connection’s transactions managed by the application server (via JTA) and one datasource that has connections that do not participate in global (JTA) transactions. JobStoreCMT is appropriate when applications are using JTA transactions (such as via EJB Session Beans) to perform their work.
The JobStore is selected by setting the org.quartz.jobStore.class
property as such:
Setting The Scheduler’s JobStore
to JobStoreCMT
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
JobStoreCMT
can be tuned with the following properties:
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.jobStore.driverDelegateClass | yes | string | null |
org.quartz.jobStore.dataSource | yes | string | null |
org.quartz.jobStore.nonManagedTXDataSource | yes | string | null |
org.quartz.jobStore.tablePrefix | no | string | "QRTZ_" |
org.quartz.jobStore.useProperties | no | boolean | false |
org.quartz.jobStore.misfireThreshold | no | int | 60000 |
org.quartz.jobStore.isClustered | no | boolean | false |
org.quartz.jobStore.clusterCheckinInterval | no | long | 15000 |
org.quartz.jobStore.maxMisfiresToHandleAtATime | no | int | 20 |
org.quartz.jobStore.dontSetAutoCommitFalse | no | boolean | false |
org.quartz.jobStore.dontSetNonManagedTXConnectionAutoCommitFalse | no | boolean | false |
org.quartz.jobStore.selectWithLockSQL | no | string | "SELECT * FROM {0}LOCKS WHERE SCHED_NAME = {1} AND LOCK_NAME = ? FOR UPDATE" |
org.quartz.jobStore.txIsolationLevelSerializable | no | boolean | false |
org.quartz.jobStore.txIsolationLevelReadCommitted | no | boolean | false |
org.quartz.jobStore.acquireTriggersWithinLock | no | boolean | false (or true - see doc below) |
org.quartz.jobStore.lockHandler.class | no | string | null |
org.quartz.jobStore.driverDelegateInitString | no | string | null |
org.quartz.jobStore.driverDelegateClass
Driver delegates understand the particular 'dialects' of varies database systems. Possible choices include:
-
org.quartz.impl.jdbcjobstore.StdJDBCDelegate
(for fully JDBC-compliant drivers) -
org.quartz.impl.jdbcjobstore.MSSQLDelegate
(for Microsoft SQL Server, and Sybase) -
org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
-
org.quartz.impl.jdbcjobstore.WebLogicDelegate
(for WebLogic drivers) -
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
-
org.quartz.impl.jdbcjobstore.oracle.WebLogicOracleDelegate
(for Oracle drivers used within Weblogic) -
org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate
(for Oracle drivers used within Weblogic) -
org.quartz.impl.jdbcjobstore.CloudscapeDelegate
-
org.quartz.impl.jdbcjobstore.DB2v6Delegate
-
org.quartz.impl.jdbcjobstore.DB2v7Delegate
-
org.quartz.impl.jdbcjobstore.DB2v8Delegate
-
org.quartz.impl.jdbcjobstore.HSQLDBDelegate
-
org.quartz.impl.jdbcjobstore.PointbaseDelegate
-
org.quartz.impl.jdbcjobstore.SybaseDelegate
Note that many databases are known to work with the StdJDBCDelegate, while others are known to work with delegates for other databases, for example Derby works well with the Cloudscape delegate (no surprise there).
org.quartz.jobStore.dataSource
The value of this property must be the name of one the DataSources defined in the configuration properties file. For JobStoreCMT, it is required that this DataSource contains connections that are capable of participating in JTA (e.g. container-managed) transactions. This typically means that the DataSource will be configured and maintained within and by the application server, and Quartz will obtain a handle to it via JNDI. See the ConfigDataSources configuration docs for DataSources for more information.
org.quartz.jobStore.nonManagedTXDataSource
JobStoreCMT requires a (second) datasource that contains connections that will not be part of container-managed transactions. The value of this property must be the name of one the DataSources defined in the configuration properties file. This datasource must contain non-CMT connections, or in other words, connections for which it is legal for Quartz to directly call commit() and rollback() on.
org.quartz.jobStore.tablePrefix
JDBCJobStore’s "table prefix" property is a string equal to the prefix given to Quartz’s tables that were created in your database. You can have multiple sets of Quartz’s tables within the same database if they use different table prefixes.
org.quartz.jobStore.useProperties
The "use properties" flag instructs JDBCJobStore that all values in JobDataMaps will be Strings, and therefore can be stored as name-value pairs, rather than storing more complex objects in their serialized form in the BLOB column. This is can be handy, as you avoid the class versioning issues that can arise from serializing your non-String classes into a BLOB.
org.quartz.jobStore.misfireThreshold
The the number of milliseconds the scheduler will 'tolerate' a trigger to pass its next-fire-time by, before being considered "misfired". The default value (if you don’t make an entry of this property in your configuration) is 60000 (60 seconds).
org.quartz.jobStore.isClustered
Set to "true" in order to turn on clustering features. This property must be set to "true" if you are having multiple instances of Quartz use the same set of database tables… otherwise you will experience havoc. See the configuration docs for clustering for more information.
org.quartz.jobStore.clusterCheckinInterval
Set the frequency (in milliseconds) at which this instance "checks-in"* with the other instances of the cluster. Affects the quickness of detecting failed instances.
org.quartz.jobStore.maxMisfiresToHandleAtATime
The maximum number of misfired triggers the jobstore will handle in a given pass. Handling many (more than a couple dozen) at once can cause the database tables to be locked long enough that the performance of firing other (not yet misfired) triggers may be hampered.
org.quartz.jobStore.dontSetAutoCommitFalse
Setting this parameter to "true" tells Quartz not to call setAutoCommit(false) on connections obtained from the DataSource(s). This can be helpful in a few situations, such as if you have a driver that complains if it is called when it is already off. This property defaults to false, because most drivers require that setAutoCommit(false) is called.
org.quartz.jobStore.dontSetNonManagedTXConnectionAutoCommitFalse
The same as the property org.quartz.jobStore.dontSetAutoCommitFalse, except that it applies to the nonManagedTXDataSource.
org.quartz.jobStore.selectWithLockSQL
Must be a SQL string that selects a row in the "LOCKS" table and places a lock on the row. If not set, the default is "SELECT * FROM {0}LOCKS WHERE SCHED_NAME = {1} AND LOCK_NAME = ? FOR UPDATE", which works for most databases. The "{0}" is replaced during run-time with the TABLE_PREFIX that you configured above. The "{1}" is replaced with the scheduler’s name.
org.quartz.jobStore.txIsolationLevelSerializable
A value of "true" tells Quartz to call setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) on JDBC connections. This can be helpful to prevent lock timeouts with some databases under high load, and "long-lasting" transactions.
org.quartz.jobStore.txIsolationLevelReadCommitted
When set to "true", this property tells Quartz to call setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED) on the non-managed JDBC connections. This can be helpful to prevent lock timeouts with some databases (such as DB2) under high load, and "long-lasting" transactions.
org.quartz.jobStore.acquireTriggersWithinLock
Whether or not the acquisition of next triggers to fire should occur within an explicit database lock. This was once necessary (in previous versions of Quartz) to avoid dead-locks with particular databases, but is no longer considered necessary, hence the default value is "false".
If "org.quartz.scheduler.batchTriggerAcquisitionMaxCount" is set to > 1, and JDBC JobStore is used, then this property must be set to "true" to avoid data corruption (as of Quartz 2.1.1 "true" is now the default if batchTriggerAcquisitionMaxCount is set > 1).
org.quartz.jobStore.lockHandler.class
The class name to be used to produce an instance of a org.quartz.impl.jdbcjobstore.Semaphore
to be used for locking control on the job store data. This is an advanced configuration feature, which should not be used by most users. By default, Quartz will select the most appropriate (pre-bundled) Semaphore implementation to use. org.quartz.impl.jdbcjobstore.UpdateLockRowSemaphore
QUARTZ-497 may be of interest to MS SQL Server users. See QUARTZ-441.
org.quartz.jobStore.driverDelegateInitString
A pipe-delimited list of properties (and their values) that can be passed to the DriverDelegate during initialization time.
The format of the string is as such:
settingName=settingValue|otherSettingName=otherSettingValue|...
The StdJDBCDelegate and all of its descendants (all delegates that ship with Quartz) support a property called 'triggerPersistenceDelegateClasses' which can be set to a comma-separated list of classes that implement the TriggerPersistenceDelegate interface for storing custom trigger types. See the Java classes SimplePropertiesTriggerPersistenceDelegateSupport and SimplePropertiesTriggerPersistenceDelegateSupport for examples of writing a persistence delegate for a custom trigger.
If you’re using JDBC-Jobstore, you’ll be needing a DataSource for its use (or two DataSources, if you’re using JobStoreCMT).
DataSources can be configured in three ways:
-
All pool properties specified in the quartz.properties file, so that Quartz can create the DataSource itself.
-
The JNDI location of an application server managed Datasource can be specified, so that Quartz can use it.
-
Custom defined org.quartz.utils.ConnectionProvider implementations.
It is recommended that your Datasource max connection size be configured to be at least the number of worker threads in the thread pool plus three. You may need additional connections if your application is also making frequent calls to the scheduler API. If you are using JobStoreCMT, the "non managed" datasource should have a max connection size of at least four.
Each DataSource you define (typically one or two) must be given a name, and the properties you define for each must contain that name, as shown below. The DataSource’s "NAME" can be anything you want, and has no meaning other than being able to identify it when it is assigned to the JDBCJobStore.
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.dataSource.NAME.driver | yes | String | null |
org.quartz.dataSource.NAME.URL | yes | String | null |
org.quartz.dataSource.NAME.user | no | String | "" |
org.quartz.dataSource.NAME.password | no | String | "" |
org.quartz.dataSource.NAME.maxConnections | no | int | 10 |
org.quartz.dataSource.NAME.validationQuery | no | String | null |
org.quartz.dataSource.NAME.idleConnectionValidationSeconds | no | int | 50 |
org.quartz.dataSource.NAME.validateOnCheckout | no | boolean | false |
org.quartz.dataSource.NAME.discardIdleConnectionsSeconds | no | int | 0 (disabled) |
org.quartz.dataSource.NAME.driver
Must be the java class name of the JDBC driver for your database.
org.quartz.dataSource.NAME.URL
The connection URL (host, port, etc.) for connection to your database.
org.quartz.dataSource.NAME.user
The user name to use when connecting to your database.
org.quartz.dataSource.NAME.password
The password to use when connecting to your database.
org.quartz.dataSource.NAME.maxConnections
The maximum number of connections that the DataSource can create in it’s pool of connections.
org.quartz.dataSource.NAME.validationQuery
Is an optional SQL query string that the DataSource can use to detect and replace failed/corrupt connections. For example an oracle user might choose "select table_name from user_tables" - which is a query that should never fail - unless the connection is actually bad.
org.quartz.dataSource.NAME.idleConnectionValidationSeconds
The number of seconds between tests of idle connections - only enabled if the validation query property is set. Default is 50 seconds.
org.quartz.dataSource.NAME.validateOnCheckout
Whether the database sql query to validate connections should be executed every time a connection is retrieved from the pool to ensure that it is still valid. If false, then validation will occur on check-in. Default is false.
org.quartz.dataSource.NAME.discardIdleConnectionsSeconds
Discard connections after they have been idle this many seconds. 0 disables the feature. Default is 0.
Example of a Quartz-defined DataSource
org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@10.0.1.23:1521:demodb org.quartz.dataSource.myDS.user = myUser org.quartz.dataSource.myDS.password = myPassword org.quartz.dataSource.myDS.maxConnections = 30
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.dataSource.NAME.jndiURL | yes | String | null |
org.quartz.dataSource.NAME.java.naming.factory.initial | no | String | null |
org.quartz.dataSource.NAME.java.naming.provider.url | no | String | null |
org.quartz.dataSource.NAME.java.naming.security.principal | no | String | null |
org.quartz.dataSource.NAME.java.naming.security.credentials | no | String | null |
org.quartz.dataSource.NAME.jndiURL
The JNDI URL for a DataSource that is managed by your application server.
org.quartz.dataSource.NAME.java.naming.factory.initial
The (optional) class name of the JNDI InitialContextFactory that you wish to use.
org.quartz.dataSource.NAME.java.naming.provider.url
The (optional) URL for connecting to the JNDI context.
org.quartz.dataSource.NAME.java.naming.security.principal
The (optional) user principal for connecting to the JNDI context.
org.quartz.dataSource.NAME.java.naming.security.credentials
The (optional) user credentials for connecting to the JNDI context.
Example of a Datasource referenced from an Application Server
org.quartz.dataSource.myOtherDS.jndiURL=jdbc/myDataSource org.quartz.dataSource.myOtherDS.java.naming.factory.initial=com.evermind.server.rmi.RMIInitialContextFactory org.quartz.dataSource.myOtherDS.java.naming.provider.url=ormi:<span class="code-comment">//localhost </span>org.quartz.dataSource.myOtherDS.java.naming.security.principal=admin org.quartz.dataSource.myOtherDS.java.naming.security.credentials=123
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.dataSource.NAME.connectionProvider.class | yes | String (class name) | null |
org.quartz.dataSource.NAME.connectionProvider.class
The class name of the ConnectionProvider to use. After instantiating the class, Quartz can automatically set configuration properties on the instance, bean-style.
Example of Using a Custom ConnectionProvider
Implementation
org.quartz.dataSource.myCustomDS.connectionProvider.class = com.foo.FooConnectionProvider org.quartz.dataSource.myCustomDS.someStringProperty = someValue org.quartz.dataSource.myCustomDS.someIntProperty = 5
Quartz’s clustering features bring both high availability and scalability to your scheduler via fail-over and load balancing functionality.
Clustering currently only works with the JDBC-Jobstore (JobStoreTX or JobStoreCMT), and essentially works by having each node of the cluster share the same database.
Load-balancing occurs automatically, with each node of the cluster firing jobs as quickly as it can. When a trigger’s firing time occurs, the first node to acquire it (by placing a lock on it) is the node that will fire it.
Only one node will fire the job for each firing. What I mean by that is, if the job has a repeating trigger that tells it to fire every 10 seconds, then at 12:00:00 exactly one node will run the job, and at 12:00:10 exactly one node will run the job, etc. It won’t necessarily be the same node each time - it will more or less be random which node runs it. The load balancing mechanism is near-random for busy schedulers (lots of triggers) but favors the same node for non-busy (e.g. few triggers) schedulers.
Fail-over occurs when one of the nodes fails while in the midst of executing one or more jobs. When a node fails, the other nodes detect the condition and identify the jobs in the database that were in progress within the failed node. Any jobs marked for recovery (with the "requests recovery" property on the JobDetail) will be re-executed by the remaining nodes. Jobs not marked for recovery will simply be freed up for execution at the next time a related trigger fires.
The clustering feature works best for scaling out long-running and/or cpu-intensive jobs (distributing the work-load over multiple nodes). If you need to scale out to support thousands of short-running (e.g 1 second) jobs, consider partitioning the set of jobs by using multiple distinct schedulers (including multiple clustered schedulers for HA). The scheduler makes use of a cluster-wide lock, a pattern that degrades performance as you add more nodes (when going beyond about three nodes - depending upon your database’s capabilities, etc.).
Enable clustering by setting the "org.quartz.jobStore.isClustered" property to "true". Each instance in the cluster should use the same copy of the quartz.properties file. Exceptions of this would be to use properties files that are identical, with the following allowable exceptions: Different thread pool size, and different value for the "org.quartz.scheduler.instanceId" property. Each node in the cluster MUST have a unique instanceId, which is easily done (without needing different properties files) by placing "AUTO" as the value of this property. See the info about the configuration properties of JDBC-JobStore for more information.
NOTE: Never run clustering on separate machines, unless their clocks are synchronized using some form of time-sync service (daemon) that runs very regularly (the clocks must be within a second of each other). See https://www.nist.gov/pml/time-and-frequency-division/services/internet-time-service-its if you are unfamiliar with how to do this.
NOTE: Never start (scheduler.start()) a non-clustered instance against the same set of database tables that any other instance is running (start()ed) against. You may get serious data corruption, and will definitely experience erratic behavior.
Example Properties For A Clustered Scheduler
#============================================================================ # Configure Main Scheduler Properties #============================================================================ org.quartz.scheduler.instanceName = MyClusteredScheduler org.quartz.scheduler.instanceId = AUTO #============================================================================ # Configure ThreadPool #============================================================================ org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 25 org.quartz.threadPool.threadPriority = 5 #============================================================================ # Configure JobStore #============================================================================ org.quartz.jobStore.misfireThreshold = 60000 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.OracleDelegate org.quartz.jobStore.useProperties = <span class="code-keyword">false</span> org.quartz.jobStore.dataSource = myDS org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.isClustered = <span class="code-keyword">true</span> org.quartz.jobStore.clusterCheckinInterval = 20000 #============================================================================ # Configure Datasources #============================================================================ org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@polarbear:1521:dev org.quartz.dataSource.myDS.user = quartz org.quartz.dataSource.myDS.password = quartz org.quartz.dataSource.myDS.maxConnections = 5 org.quartz.dataSource.myDS.validationQuery=select 0 from dual
TerracottaJobStore
is used to store scheduling information (job, triggers and calendars) within a Terracotta server.
TerracottaJobStore
is much more performant than utilizing a database for storing scheduling data (via JDBC-JobStore),
and yet offers clustering features such as load-balancing and fail-over.
You may want to consider implications of how you setup your Terracotta server, particularly configuration options that turn on features such as storing data on disk, utilization of fsync, and running an array of Terracotta servers for HA.
The clustering feature works best for scaling out long-running and/or cpu-intensive jobs (distributing the work-load over multiple nodes). If you need to scale out to support thousands of short-running (e.g 1 second) jobs, consider partitioning the set of jobs by using multiple distinct schedulers. Using more than one scheduler currently forces the use of a cluster-wide lock, a pattern that degrades performance as you add more clients.
More information about this JobStore and Terracotta can be found at http://www.terracotta.org/quartz
TerracottaJobStore
is selected by setting the org.quartz.jobStore.class
property as such:
Setting The Scheduler’s JobStore
to TerracottaJobStore
org.quartz.jobStore.class = org.terracotta.quartz.TerracottaJobStore
TerracottaJobStore
can be tuned with the following properties:
Property Name | Required | Type | Default Value |
---|---|---|---|
org.quartz.jobStore.tcConfigUrl | yes | string | |
org.quartz.jobStore.misfireThreshold | no | int | 60000 |
org.quartz.jobStore.tcConfigUrl
The host and port identifying the location of the Terracotta server to connect to, such as "localhost:9510".
org.quartz.jobStore.misfireThreshold
The the number of milliseconds the scheduler will 'tolerate' a trigger to pass its next-fire-time by, before being considered "misfired". The default value (if you don’t make an entry of this property in your configuration) is 60000 (60 seconds).