Quartz Enterprise Job Scheduler - A richly featured, open source job scheduling library that can be integrated within virtually any Java application
—
Job creation, configuration, and lifecycle management in Quartz. This covers the Job interface that defines work to be executed, JobDetail for job metadata and configuration, JobBuilder for fluent job creation, and JobExecutionContext for runtime information.
The core interface that defines the work to be executed. All application jobs must implement this interface.
/**
* Interface to be implemented by classes which represent a 'job' to be performed
* Job instances must have a public no-argument constructor
*/
interface Job {
/**
* Execute the job. Called by the scheduler when a trigger fires
* @param context provides runtime information about the job execution
* @throws JobExecutionException if job execution fails or needs special handling
*/
void execute(JobExecutionContext context) throws JobExecutionException;
}Usage Examples:
// Simple job implementation
public class ReportJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Generating report at: " + new Date());
// Access job data
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String reportType = dataMap.getString("reportType");
try {
generateReport(reportType);
} catch (Exception e) {
// Wrap and rethrow as JobExecutionException
throw new JobExecutionException("Report generation failed", e);
}
}
private void generateReport(String type) {
// Report generation logic
}
}
// Job with error handling and retry logic
public class DataProcessingJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
processData();
} catch (TransientException e) {
// Retry immediately
JobExecutionException jee = new JobExecutionException(e);
jee.setRefireImmediately(true);
throw jee;
} catch (PermanentException e) {
// Unschedule this trigger
JobExecutionException jee = new JobExecutionException(e);
jee.setUnscheduleFiringTrigger(true);
throw jee;
}
}
private void processData() throws TransientException, PermanentException {
// Data processing logic
}
}Conveys the details of a Job instance, including identity, job class, data, and behavior configuration.
/**
* Conveys the detail properties of a job instance
*/
interface JobDetail extends Serializable, Cloneable {
/**
* Get the unique key that identifies this job
* @return the job's key (name and group)
*/
JobKey getKey();
/**
* Get the human-readable description of the job
* @return job description or null if none set
*/
String getDescription();
/**
* Get the job implementation class
* @return the class that implements the Job interface
*/
Class<? extends Job> getJobClass();
/**
* Get the job data map containing job state and parameters
* @return job data map (never null)
*/
JobDataMap getJobDataMap();
/**
* Check if the job is durable (persists without triggers)
* @return true if job should persist when no triggers reference it
*/
boolean isDurable();
/**
* Check if job data should be persisted after execution
* @return true if JobDataMap changes should be persisted
*/
boolean isPersistJobDataAfterExecution();
/**
* Check if concurrent execution of this job is disallowed
* @return true if only one instance of this job should execute at a time
*/
boolean isConcurrentExecutionDisallowed();
/**
* Check if the job requests recovery after scheduler failure
* @return true if job should be re-executed after scheduler restart
*/
boolean requestsRecovery();
/**
* Get a JobBuilder configured to produce a JobDetail identical to this one
* @return builder for creating similar job details
*/
JobBuilder getJobBuilder();
}Fluent builder for creating JobDetail instances with type safety and clear configuration.
/**
* Builder for JobDetail instances using fluent interface
*/
class JobBuilder {
/**
* Create a new JobBuilder
* @return new builder instance
*/
static JobBuilder newJob();
/**
* Create a new JobBuilder with specified job class
* @param jobClass the Job implementation class
* @return new builder instance
*/
static JobBuilder newJob(Class<? extends Job> jobClass);
/**
* Set the job's identity using name only (uses DEFAULT_GROUP)
* @param name the job name
* @return this builder for method chaining
*/
JobBuilder withIdentity(String name);
/**
* Set the job's identity using name and group
* @param name the job name
* @param group the job group
* @return this builder for method chaining
*/
JobBuilder withIdentity(String name, String group);
/**
* Set the job's identity using a JobKey
* @param jobKey the complete job key
* @return this builder for method chaining
*/
JobBuilder withIdentity(JobKey jobKey);
/**
* Set the job's description
* @param jobDescription human-readable description
* @return this builder for method chaining
*/
JobBuilder withDescription(String jobDescription);
/**
* Set the job's implementation class
* @param jobClazz the Job implementation class
* @return this builder for method chaining
*/
JobBuilder ofType(Class<? extends Job> jobClazz);
/**
* Request recovery for this job (default false)
* @return this builder for method chaining
*/
JobBuilder requestRecovery();
/**
* Set recovery request flag
* @param jobShouldRecover if true, job will be re-executed after scheduler failure
* @return this builder for method chaining
*/
JobBuilder requestRecovery(boolean jobShouldRecover);
/**
* Make the job durable (persists without triggers)
* @return this builder for method chaining
*/
JobBuilder storeDurably();
/**
* Set job durability
* @param jobDurability if true, job persists when no triggers reference it
* @return this builder for method chaining
*/
JobBuilder storeDurably(boolean jobDurability);
/**
* Add a single data value to the job's data map
* @param key the data key
* @param value the data value
* @return this builder for method chaining
*/
JobBuilder usingJobData(String key, String value);
/**
* Add integer data to the job's data map
* @param key the data key
* @param value the integer value
* @return this builder for method chaining
*/
JobBuilder usingJobData(String key, Integer value);
/**
* Add long data to the job's data map
* @param key the data key
* @param value the long value
* @return this builder for method chaining
*/
JobBuilder usingJobData(String key, Long value);
/**
* Add float data to the job's data map
* @param key the data key
* @param value the float value
* @return this builder for method chaining
*/
JobBuilder usingJobData(String key, Float value);
/**
* Add double data to the job's data map
* @param key the data key
* @param value the double value
* @return this builder for method chaining
*/
JobBuilder usingJobData(String key, Double value);
/**
* Add boolean data to the job's data map
* @param key the data key
* @param value the boolean value
* @return this builder for method chaining
*/
JobBuilder usingJobData(String key, Boolean value);
/**
* Set the job's entire data map
* @param newJobDataMap the job data map
* @return this builder for method chaining
*/
JobBuilder usingJobData(JobDataMap newJobDataMap);
/**
* Replace the job's data map (clears existing data)
* @param newJobDataMap the replacement job data map
* @return this builder for method chaining
*/
JobBuilder setJobData(JobDataMap newJobDataMap);
/**
* Build the JobDetail instance
* @return the configured JobDetail
*/
JobDetail build();
}Usage Examples:
// Basic job creation
JobDetail job = newJob(ReportJob.class)
.withIdentity("dailyReport", "reports")
.withDescription("Generate daily sales report")
.build();
// Job with data and special configuration
JobDetail processingJob = newJob(DataProcessingJob.class)
.withIdentity("dataProcessor", "processing")
.withDescription("Process incoming data files")
.usingJobData("inputDir", "/data/input")
.usingJobData("outputDir", "/data/output")
.usingJobData("maxFiles", 100)
.usingJobData("timeout", 30000L)
.usingJobData("retryEnabled", true)
.requestRecovery()
.storeDurably()
.build();
// Job using JobDataMap
JobDataMap dataMap = new JobDataMap();
dataMap.put("config", configObject);
dataMap.put("database", databaseConnection);
JobDetail complexJob = newJob(ComplexProcessingJob.class)
.withIdentity("complexProcessor")
.setJobData(dataMap)
.build();
// Static import for cleaner syntax
import static org.quartz.JobBuilder.*;
JobDetail cleanJob = newJob(MyJob.class)
.withIdentity("myJob", "myGroup")
.build();Container for job state and parameters that can be accessed during job execution.
/**
* Holds state information for Job instances
* Extends Map interface for convenient data access
*/
class JobDataMap extends StringKeyDirtyFlagMap {
/**
* Create an empty JobDataMap
*/
JobDataMap();
/**
* Create a JobDataMap with initial data
* @param map initial data to populate the map
*/
JobDataMap(Map<?, ?> map);
/**
* Get string value with type safety
* @param key the data key
* @return string value or null
*/
String getString(String key);
/**
* Get boolean value with type conversion
* @param key the data key
* @return boolean value or false if not found/convertible
*/
boolean getBooleanValue(String key);
/**
* Get Boolean object value
* @param key the data key
* @return Boolean object or null
*/
Boolean getBoolean(String key);
/**
* Get char value with type conversion
* @param key the data key
* @return char value or space if not found/convertible
*/
char getCharValue(String key);
/**
* Get Character object value
* @param key the data key
* @return Character object or null
*/
Character getCharacter(String key);
/**
* Get double value with type conversion
* @param key the data key
* @return double value or 0.0 if not found/convertible
*/
double getDoubleValue(String key);
/**
* Get Double object value
* @param key the data key
* @return Double object or null
*/
Double getDouble(String key);
/**
* Get float value with type conversion
* @param key the data key
* @return float value or 0.0f if not found/convertible
*/
float getFloatValue(String key);
/**
* Get Float object value
* @param key the data key
* @return Float object or null
*/
Float getFloat(String key);
/**
* Get int value with type conversion
* @param key the data key
* @return int value or 0 if not found/convertible
*/
int getIntValue(String key);
/**
* Get Integer object value
* @param key the data key
* @return Integer object or null
*/
Integer getInt(String key);
/**
* Get long value with type conversion
* @param key the data key
* @return long value or 0L if not found/convertible
*/
long getLongValue(String key);
/**
* Get Long object value
* @param key the data key
* @return Long object or null
*/
Long getLong(String key);
}Usage Examples:
// Creating and populating JobDataMap
JobDataMap dataMap = new JobDataMap();
dataMap.put("reportType", "sales");
dataMap.put("quarter", 1);
dataMap.put("includeCharts", true);
dataMap.put("timeout", 5000L);
// Type-safe access in job
public class ReportJob implements Job {
public void execute(JobExecutionContext context) {
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String reportType = dataMap.getString("reportType");
int quarter = dataMap.getIntValue("quarter");
boolean includeCharts = dataMap.getBooleanValue("includeCharts");
long timeout = dataMap.getLongValue("timeout");
generateReport(reportType, quarter, includeCharts, timeout);
}
}
// Merging with trigger data
JobDataMap mergedData = context.getMergedJobDataMap();
// Contains both job and trigger dataProvides runtime context and information to executing jobs.
/**
* Context provides runtime information to Job instances during execution
*/
interface JobExecutionContext {
/**
* Get the scheduler instance that is executing this job
* @return the scheduler instance
*/
Scheduler getScheduler();
/**
* Get the trigger that fired this job
* @return the firing trigger
*/
Trigger getTrigger();
/**
* Get the calendar associated with the trigger (if any)
* @return the calendar or null
*/
Calendar getCalendar();
/**
* Check if this is a recovery execution
* @return true if job is being recovered after scheduler failure
*/
boolean isRecovering();
/**
* Get the retry count for this execution
* @return number of times this job has been retried
*/
int getRefireCount();
/**
* Get the merged job data (job + trigger data)
* @return merged job data map
*/
JobDataMap getMergedJobDataMap();
/**
* Get the job detail for this execution
* @return the job detail
*/
JobDetail getJobDetail();
/**
* Get the job instance being executed
* @return the job instance
*/
Job getJobInstance();
/**
* Get the time this job was scheduled to fire
* @return the scheduled fire time
*/
Date getScheduledFireTime();
/**
* Get the actual time this job fired
* @return the actual fire time
*/
Date getFireTime();
/**
* Get the next time the trigger will fire
* @return the next fire time or null if no more firings
*/
Date getNextFireTime();
/**
* Get the previous time the trigger fired
* @return the previous fire time or null if first firing
*/
Date getPreviousFireTime();
/**
* Get the unique fire instance ID
* @return unique identifier for this execution
*/
String getFireInstanceId();
/**
* Get the result object from job execution
* @return the result object or null
*/
Object getResult();
/**
* Set the result object for this job execution
* @param result the result object
*/
void setResult(Object result);
/**
* Get the job run time (how long the job executed)
* @return job runtime in milliseconds
*/
long getJobRunTime();
}Usage Examples:
public class ContextAwareJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
// Access scheduler information
Scheduler scheduler = context.getScheduler();
String schedulerName = scheduler.getSchedulerName();
// Access trigger information
Trigger trigger = context.getTrigger();
System.out.println("Fired by trigger: " + trigger.getKey());
// Access timing information
Date scheduledTime = context.getScheduledFireTime();
Date actualTime = context.getFireTime();
long delay = actualTime.getTime() - scheduledTime.getTime();
System.out.println("Execution delay: " + delay + "ms");
// Check if this is a recovery
if (context.isRecovering()) {
System.out.println("This is a recovery execution");
}
// Access merged data (job + trigger data)
JobDataMap mergedData = context.getMergedJobDataMap();
String config = mergedData.getString("config");
// Set result for listeners
context.setResult("Job completed successfully");
// Get unique execution ID
String fireId = context.getFireInstanceId();
System.out.println("Execution ID: " + fireId);
}
}Unique identifier for job instances combining name and group.
/**
* Uniquely identifies a JobDetail within a Scheduler
*/
class JobKey extends Key<JobKey> {
/**
* Create a JobKey with name in the default group
* @param name the job name
*/
JobKey(String name);
/**
* Create a JobKey with name and group
* @param name the job name
* @param group the job group
*/
JobKey(String name, String group);
/**
* Static factory method for JobKey with default group
* @param name the job name
* @return new JobKey instance
*/
static JobKey jobKey(String name);
/**
* Static factory method for JobKey with group
* @param name the job name
* @param group the job group
* @return new JobKey instance
*/
static JobKey jobKey(String name, String group);
}Usage Examples:
// Creating job keys
JobKey key1 = new JobKey("myJob");
JobKey key2 = new JobKey("myJob", "myGroup");
// Using static factory methods (preferred)
JobKey key3 = jobKey("reportJob", "reports");
JobKey key4 = jobKey("maintenanceJob"); // Uses DEFAULT_GROUP
// Using in job operations
scheduler.deleteJob(jobKey("oldJob", "cleanup"));
scheduler.triggerJob(jobKey("reportJob", "reports"));
boolean exists = scheduler.checkExists(jobKey("myJob", "myGroup"));Install with Tessl CLI
npx tessl i tessl/maven-org-quartz-scheduler--quartz