Technical reference
The Job plugin provides a small set of base classes for building an asynchronous job execution pipeline: a Job to hold the work and its audit trail, a JobStatus enum to represent where that work stands, a JobRunner to hold configuration and do the work, and a JobExecutionTask to find pending jobs and run them on a schedule.
Dependencies
| Dependency | Description |
|---|---|
com.psddev:cms-db | Provides Content and the ToolUi annotations used to control how a job appears in the CMS tool. |
com.psddev:dari-db | Provides Record, Query, and Modification, which Job and its supporting classes build on. |
com.psddev:dari-util | Provides Task and RepeatingTask, which JobExecutionTask extends. |
joda-time | Used by JobExecutionTask to calculate its run schedule. |
Installation
- Maven
- Gradle
- Gradle (Kotlin DSL)
<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>job</artifactId>
<version>1.2.0</version>
</dependency>
// Requires Brightspot 4.5 or later.
implementation 'com.brightspot.job:job:1.2.0'
// Requires Brightspot 4.5 or later.
implementation("com.brightspot.job:job:1.2.0")
API reference
JobStatus
Interface that a job's status enum implements. Each enum value maps to a StatusProperty that tells the job execution system how to treat jobs in that status—whether they are still pending, should be retried, are polling for an external result, or have reached a terminal state.
| Member | Description |
|---|---|
JobStatus#getProperty() | Returns the StatusProperty for this status. Required. More than one status can share the same property. |
JobStatus.StatusProperty | Enum of PRIORITY, PENDING, RETRY, POLLING, SUCCESS, PERMANENT_FAILURE, MAX_RETRIES. |
JobStatus#isPending(), #isPriority(), #isRetriesAllowed(), #isPolling(), #isMaxRetriesExceeded(), #isPermanentFailure() | Convenience checks against the corresponding StatusProperty. |
JobStatus#isSuccessfulCompletion() | True for SUCCESS statuses. |
JobStatus#isUnsuccessfulCompletion() | True for PERMANENT_FAILURE or MAX_RETRIES statuses. |
JobStatus.values(Class) | Static helper that returns all enum constants of a JobStatus class, regardless of property. |
JobStatus.priorityStatuses(Class), #pendingStatuses(Class), #retryStatuses(Class), #pollingStatuses(Class), #maxRetriesStatuses(Class) | Static helpers that return the enum constants of a JobStatus class matching the corresponding StatusProperty. |
JobStatus.find(Class, String) | Static helper that looks up a status by its enum name. |
Job
Abstract Record representing a single queued unit of work, its current status, and its activity log. Extend this class for each distinct kind of job in your application.
Must implement:
| Method | Description |
|---|---|
Job#getStatus() | Returns the status of the most recent execution. |
Job#setStatus(S) | Sets the status of the most recent execution. |
Provided:
| Method | Description |
|---|---|
Job#getQueueDate() | The date the job was queued. Set automatically on first save. |
Job#getLastActivityDate() | The date of the most recent status change. |
Job#getCompletionStatus() | SUCCESS or FAILURE once the job reaches a terminal state, null otherwise. Calculated automatically from #getStatus() on save. |
Job#getLog() | The ordered list of JobActivity entries recorded for this job. |
Job#getAttempts() | The number of execution attempts logged so far. |
Job#getRetries(Class) | The number of consecutive retry-eligible attempts since the last non-retry status. |
Job#getSecondsInQueue() | Seconds between the queue date and the last activity date. |
Job#getRunner() / #setRunner(JobRunner) | The JobRunner responsible for executing this job. |
Job#logActivity(JobExecutionResult, Consumer<String>) | Records the result of an execution, updates the job's status and last activity date, and invokes the supplied logger. Adds a new JobActivity entry to #getLog(), unless the result repeats the same polling status and message as the previous entry, in which case it increments a polling counter instead of adding a duplicate entry. Called by JobExecutionTask; not typically called directly from a JobRunner. |
Job#afterLogActivity(JobExecutionResult, Consumer<String>) | Override to run custom logic immediately after an activity is logged. No-op by default. |
Job#calculateCompletionStatus() | Derives SUCCESS, FAILURE, or null from the current status's isSuccessfulCompletion() / isUnsuccessfulCompletion(). |
JobRunner
Interface for the object that holds configuration for a group of jobs and does the actual work. JobRunner implementations are Records saved in the database so JobExecutionTask can find and enable or disable them without a deploy.
| Method | Description |
|---|---|
JobRunner#execute(Job, Task) | Does the job. parentTask is the JobExecutionTask supervising the execution. Return the resulting status and a message; the task applies the status to the job. |
JobRunner#isEnabled() | Return false to skip this runner entirely. |
JobRunner#getTaskHostOrIpAddress() | The hostname or IP address jobs for this runner are allowed to run on. Must resolve to the current host's IP address for the runner to execute; if null, or if it does not resolve to the current host, the runner is skipped on every execution cycle. |
JobRunner#getMaxNumberOfRetries() | Maximum retry attempts for statuses with a RETRY property. |
JobRunner#getMinSecondsBetweenRetries() | Minimum delay between retry attempts. |
JobRunner#getMinSecondsBetweenPolls() | Minimum delay between polling attempts. |
JobRunner#getParallelLevel() | Number of jobs to execute concurrently for this runner. Defaults to 1. |
JobRunner#handleException(Job, Throwable) | Return a JobExecutionResult if #execute throws. |
JobExecutionResult
The status and message returned from JobRunner#execute(Job, Task) or #handleException(Job, Throwable). JobExecutionTask applies this result to the job by calling Job#logActivity(JobExecutionResult, Consumer).
| Constructor | Description |
|---|---|
JobExecutionResult(S status, String message) | Creates a result with the given status and message. |
JobExecutionResult(S status, String message, Throwable exception) | Creates a result with the given status, and appends the exception's stack trace to the message. |
JobExecutionTask
Abstract RepeatingTask that queries for pending jobs and executes them using their configured JobRunners, in priority, pending, retry, then polling order.
Provided:
| Method | Description |
|---|---|
JobExecutionTask#jobClass() | The Job subclass to query for. Resolved automatically from the subclass's generic type parameter; override only if that parameter is not a concrete class. |
JobExecutionTask#jobRunnerClass() | The JobRunner subclass to query for. Resolved automatically the same way as #jobClass(). Can be an abstract class shared by multiple runner implementations, in which case all matching jobs run through the same task. |
JobExecutionTask#statusClass() | The JobStatus enum used to determine which statuses are pending, retryable, and so on. Resolved automatically the same way as #jobClass(). |
Configuration
Override the following methods on a JobExecutionTask subclass to change how often and how many jobs it processes:
| Method | Default | Description |
|---|---|---|
JobExecutionTask#getRunEverySeconds() | 3 | How often the task checks for jobs to run. |
JobExecutionTask#getBatchSize() | 10 | Number of pending, retry, or polling jobs fetched per runner on each run. |
JobExecutionTask#getPriorityBatchSize() | 10 | Number of priority jobs fetched per runner on each run. |
JobExecutionTask#getEnabledRunnersCacheSeconds() | 15 | How long the list of enabled job runners is cached before being requeried. |
Override JobRunner#getParallelLevel(), #getMaxNumberOfRetries(), #getMinSecondsBetweenRetries(), and #getMinSecondsBetweenPolls() to control concurrency and retry timing per runner, since JobRunner instances are database records that can be reconfigured without a deploy.
Task framework
The task framework (task-core and its companion modules) is a lower-level alternative to JobExecutionTask for scheduling repeating and cron-based work that runs outside the job queue—for example, work that has no job asset of its own, or that needs an owner other than a single shared execution loop, such as a global instance, a per-site instance, or another owning entity.
Dependencies
| Dependency | Description |
|---|---|
com.psddev:dari-util | Provides Task, which AbstractRepeatingTask extends. |
Installation
- Maven
- Gradle
- Gradle (Kotlin DSL)
<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>task-core</artifactId>
<version>1.2.0</version>
</dependency>
// Requires Brightspot 4.5 or later.
implementation 'com.brightspot.job:task-core:1.2.0'
// Requires Brightspot 4.5 or later.
implementation("com.brightspot.job:task-core:1.2.0")
Add the companion module for the scheduling style needed—cron-configuration, logging-task, import-task, or one of the global-, site-, or variable- prefixed repeating and repeating-cron task modules—the same way, using its own artifactId.
Dispatcher and Dispatchable
A Dispatcher creates and manages Dispatchables: units of work that can be submitted once, scheduled after a delay, or scheduled to repeat with a fixed delay or fixed rate. TaskDispatcher is the provided Dispatcher implementation, built on the original Brightspot RepeatingTask; avoid implementing a custom Dispatcher.
| Member | Description |
|---|---|
Dispatchable#submit() | Runs the Dispatchable immediately. No-op if it is already scheduled. |
Dispatchable#schedule(double) | Runs the Dispatchable once, after the given delay in seconds. |
Dispatchable#scheduleWithFixedDelay(double, double) | Runs the Dispatchable repeatedly, waiting the given periodic delay after each run finishes. |
Dispatchable#scheduleWithFixedRate(double, double) | Runs the Dispatchable repeatedly at the given periodic rate, regardless of how long each run takes. |
Dispatchable#pause() / #resume() | Requests that a running Dispatchable pause or resume. |
Dispatchable#stop() | Requests that a running Dispatchable stop. |
Dispatchable#isRunning(), #isPauseRequested(), #isSafeToStop() | Status checks used while managing the Dispatchable's lifecycle. |
Dispatchable#getProgress(), #getProgressIndex(), #getProgressTotal() | Progress reporting for a long-running Dispatchable. |
Dispatchable#getLastException() | The last Throwable thrown during execution. |
DispatchableConfiguration#create() | Creates a Dispatchable from this configuration. Requires the configuration class to have a public no-argument constructor. |
DispatchableConfiguration#isEnabled() | Whether the configured Dispatchable should be considered for execution. |
DispatchableConfiguration#getTaskHost() | The host the Dispatchable is allowed to run on. Falls back to the default task host configured on GlobalTaskSettings when not overridden. |
DispatchableDescriptor | Identifies a Dispatchable by its owner ID (a CmsTool, Site, or other owning entity) and Dispatchable class. |
RepeatingTask
RepeatingTask extends Dispatchable for work that reschedules itself after every run until it is stopped. It is unrelated to the original Brightspot RepeatingTask from dari-util.
| Member | Description |
|---|---|
RepeatingTask#calculateNextRunTime(Instant, Instant) | Calculates the next run time from the previous run time and the current time. |
RepeatingTask#doRepeatingTask(Instant) | Runs the task for the given run time. |
RepeatingTask#getPreviousRunTime() | An AtomicReference to the previous run time. |
AbstractRepeatingTask | Base implementation, extending Brightspot's Task. |
RepeatingTaskUtils | Utility methods for calculating run times and checking whether a RepeatingTask is allowed to run on the current task host. |
A RepeatingTask is added to TaskDispatcher's registry automatically. Assign it to an executor via DispatchableExecutor.
Cron scheduling
cron-configuration adds CronConfiguration, a contract for evaluating run times from a cron expression, independent of the rest of the task framework.
| Member | Description |
|---|---|
CronConfiguration#getCronExpression() | The cron expression that determines the task's run time. |
CronConfiguration#getLastRunTime() | The last run time, used to evaluate the next run time. |
repeating-cron-task combines RepeatingTask with CronConfiguration as RepeatingCronTask, calculating its next run time from the cron expression instead of a fixed interval.
Task ownership: global, site, and variable
RepeatingTask and RepeatingCronTask are further specialized by who owns and runs the task:
| Module | Owner | Cron support |
|---|---|---|
global-repeating-task | A single global instance (GlobalRepeatingTask) | No |
site-repeating-task | One instance per Site (SiteRepeatingTask, keyed by getSiteId()) | No |
variable-repeating-task | Any owning entity (VariableRepeatingTask) | No |
global-repeating-cron-task | A single global instance (GlobalRepeatingCronTask) | Yes |
site-repeating-cron-task | One instance per Site (SiteRepeatingCronTask) | Yes |
variable-repeating-cron-task | Any owning entity (VariableRepeatingCronTask) | Yes |
Each module provides a matching *Configuration interface (for example, SiteRepeatingTaskConfiguration#get(Site)) that generates the task name and resolves the owning entity.
Logging and importing
logging-task adds LoggingTask, a contract for a repeating task to record a TaskLog—a start and end time, plus a TaskStatus—for its most recent run. FileBackedTaskLog, and its JsonFileBackedTaskLog and PlainTextFileBackedTaskLog extensions, persist the log contents to a StorageItem.
import-task builds on logging-task for tasks that import assets from an external source:
| Member | Description |
|---|---|
ImportTask#getConfiguration() | Returns the ImportTaskConfiguration describing which Importers to run. |
ImportTaskConfiguration#getImporters() | The set of Importers this task runs. |
ImportTaskConfiguration#areImportersAvailable() | Whether the importers are currently available to run. |
Importer#doImport(T) | Imports a single item and returns a ChangeLog describing what changed. |
ImportTaskLog#getChangeLog() | The ChangeLog recorded for a run of the ImportTask. |
ChangeLog, Change, and Issue (under brightspot.task.importer.changelog) record what an import changed and any issues it encountered, for display in the task's log.
Subscribable jobs
subscribable-jobs extends Job with support for publishing a CMS notification whenever a job's status changes.
Installation
- Maven
- Gradle
- Gradle (Kotlin DSL)
<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>subscribable-jobs</artifactId>
<version>1.2.0</version>
</dependency>
// Requires Brightspot 4.5 or later.
implementation 'com.brightspot.job:subscribable-jobs:1.2.0'
// Requires Brightspot 4.5 or later.
implementation("com.brightspot.job:subscribable-jobs:1.2.0")
API reference
| Member | Description |
|---|---|
SubscribableJob#getPublisherClass() | Returns the JobPublisher class to use for this job. Must implement. |
SubscribableJob#getLogger() | Returns the Logger used to report publishing failures. Must implement. |
SubscribableJob#isEnabled() | Whether notifications are enabled for this job. true by default. |
JobPublisher<S, C> | A Publisher that sends notifications for a JobSubscription of type S regarding a SubscribableJob of type C. |
JobSubscription<J> | A ToolSubscription for a SubscribableJob of type J. Extend it to add filter criteria—for example, which statuses to notify on—and override how the notification is rendered. |
SubscribableJob publishes a notification after every Job#logActivity(JobExecutionResult, Consumer) call, immediately after Job#afterLogActivity(JobExecutionResult, Consumer) runs.
CMS Scripts
cms-scripts-core builds on the job execution framework to provide a CMS tool for running and auditing administrative scripts. See Working with CMS Scripts for the editorial workflow.
Dependencies
| Dependency | Description |
|---|---|
com.brightspot.job:job | Provides Job, JobRunner, and JobStatus, which the script job execution flow builds on. |
com.brightspot.job:subscribable-jobs | Provides SubscribableJob, which ScriptJob extends to support notifications. |
com.brightspot.job:task-core | Provides RepeatingTaskUtils, used by AbstractAsyncProcessQueryScript (from cms-scripts-async). |
Installation
- Maven
- Gradle
- Gradle (Kotlin DSL)
<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>cms-scripts-core</artifactId>
<version>1.2.0</version>
</dependency>
// Requires Brightspot 4.5 or later.
implementation 'com.brightspot.job:cms-scripts-core:1.2.0'
// Requires Brightspot 4.5 or later.
implementation("com.brightspot.job:cms-scripts-core:1.2.0")
For a base script implementation that processes a Query result set asynchronously across multiple writer threads, also add:
- Maven
- Gradle
- Gradle (Kotlin DSL)
<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>cms-scripts-async</artifactId>
<version>1.2.0</version>
</dependency>
// Requires Brightspot 4.5 or later.
implementation 'com.brightspot.job:cms-scripts-async:1.2.0'
// Requires Brightspot 4.5 or later.
implementation("com.brightspot.job:cms-scripts-async:1.2.0")
API reference
Script
The contract every script implementation must satisfy. A Script is a Recordable—its fields are rendered as the script's form in the Scripts area—and also the unit of execution logic.
| Member | Description |
|---|---|
Script#execute(ScriptJob, Task) | Runs the script's logic and returns a ScriptJobExecutionResult. Must implement. |
Script#handleException(ScriptJob, Throwable) | Handles an exception thrown by #execute. Returns PERMANENT_FAILURE with the stack trace by default. |
Script#getScriptDisplayOrder() | Where this script sorts on the Scripts page relative to other scripts. Negative values sort above the alphabetical group, positive values sort below it. Defaults to 0. |
Script#getShortLabel() | The script's label with the "Script" suffix removed. |
Script.getAllInstances() | Static helper that returns an instance of every concrete Script implementation found on the classpath. |
ScriptJob
A SubscribableJob<ScriptJobStatus> that records a single script execution: which Script ran, the ToolUser who ran it, and its duration.
| Member | Description |
|---|---|
ScriptJob#getScript() / #setScript(Script) | The Script configuration this job runs. |
ScriptJob#getUser() | The ToolUser who queued the job. |
ScriptJob#getDurationInMilliseconds() / #getDurationLabel() | The execution duration, as a raw value or a formatted label (for example, 1h2m3s). |
ScriptJob#isRunning() | Whether the job is currently executing. |
ScriptJob.queue(Script, ToolUser) | Static helper that creates and immediately saves a pending ScriptJob for the given script and user. |
ScriptJobStatus
The JobStatus enum for ScriptJob: PENDING, SUCCESS, DATABASE_EXCEPTION (retry), MAX_RETRIES, UNHANDLED_EXCEPTION (permanent failure), PERMANENT_FAILURE, INVALID_JOB (permanent failure), PAUSED and WAITING (polling), UNPAUSED (pending), and CANCELED (permanent failure).
ScriptJobRunner
The single JobRunner<ScriptJob, ScriptJobStatus> Singleton that executes every ScriptJob by delegating to its Script#execute(ScriptJob, Task). Its configuration—enabled, task host, retry, and poll settings—comes from ScriptJobTool.
ScriptJobTool
The Tool configuration for the Scripts area, edited from Sites & Settings > Legacy Settings > Brightspot Scripts.
| Member | Description | Default |
|---|---|---|
ScriptJobTool#isEnabled() | Whether script job execution is enabled. | false |
ScriptJobTool#getTaskHost() | The host script jobs are allowed to run on. Falls back to GlobalTaskSettings#getDefaultTaskHost() when not set. | — |
ScriptJobTool#getMaximumNumberOfRetries() | Maximum retry attempts for a retryable script job status. | 20 |
ScriptJobTool#getMinimumNumberOfSecondsBetweenRetries() | Minimum delay between retries. | 3 |
ScriptJobTool#getMinimumNumberOfSecondsBetweenPolls() | Minimum delay between polling attempts. | 3 |
Notifications
ScriptJobPublisher and ScriptJobSubscription are the JobPublisher and JobSubscription for ScriptJob. ScriptJobSubscription filters on a set of ScriptJobState values (a simplified grouping of terminal ScriptJobStatus values) and a set of script types, and renders the notification as an HTML summary of the script, status, queue date, duration, and user.
Permissions
ScriptPermission is an AdditionalPermission that controls which scripts a ToolRole can access, backed by a ScriptPermissionOption: AllScriptsPermissionOption, NoneScriptsPermissionOption, OnlyScriptsPermissionOption (allow-list), or AllExceptScriptsPermissionOption (deny-list).
Batch scripts
Scripts can also be grouped into a BatchScriptsProfile and run together. BatchScriptsJob, BatchScriptsJobRunner, and the rest of the com.psddev.script.batch package mirror the single-script ScriptJob classes for a profile's scripts. A BatchScriptSchedule—created via BatchScriptSchedule.schedule(BatchScriptsProfile, ToolUser, Date)—queues a profile's BatchScriptsJob once a BatchScriptScheduleTask finds it due.