Skip to main content

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​

DependencyDescription
com.psddev:cms-dbProvides Content and the ToolUi annotations used to control how a job appears in the CMS tool.
com.psddev:dari-dbProvides Record, Query, and Modification, which Job and its supporting classes build on.
com.psddev:dari-utilProvides Task and RepeatingTask, which JobExecutionTask extends.
joda-timeUsed by JobExecutionTask to calculate its run schedule.

Installation​

<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>job</artifactId>
<version>1.2.0</version>
</dependency>

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.

MemberDescription
JobStatus#getProperty()Returns the StatusProperty for this status. Required. More than one status can share the same property.
JobStatus.StatusPropertyEnum 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:

MethodDescription
Job#getStatus()Returns the status of the most recent execution.
Job#setStatus(S)Sets the status of the most recent execution.

Provided:

MethodDescription
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.

MethodDescription
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).

ConstructorDescription
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:

MethodDescription
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:

MethodDefaultDescription
JobExecutionTask#getRunEverySeconds()3How often the task checks for jobs to run.
JobExecutionTask#getBatchSize()10Number of pending, retry, or polling jobs fetched per runner on each run.
JobExecutionTask#getPriorityBatchSize()10Number of priority jobs fetched per runner on each run.
JobExecutionTask#getEnabledRunnersCacheSeconds()15How 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​

DependencyDescription
com.psddev:dari-utilProvides Task, which AbstractRepeatingTask extends.

Installation​

<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>task-core</artifactId>
<version>1.2.0</version>
</dependency>

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.

MemberDescription
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.
DispatchableDescriptorIdentifies 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.

MemberDescription
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.
AbstractRepeatingTaskBase implementation, extending Brightspot's Task.
RepeatingTaskUtilsUtility 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.

MemberDescription
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:

ModuleOwnerCron support
global-repeating-taskA single global instance (GlobalRepeatingTask)No
site-repeating-taskOne instance per Site (SiteRepeatingTask, keyed by getSiteId())No
variable-repeating-taskAny owning entity (VariableRepeatingTask)No
global-repeating-cron-taskA single global instance (GlobalRepeatingCronTask)Yes
site-repeating-cron-taskOne instance per Site (SiteRepeatingCronTask)Yes
variable-repeating-cron-taskAny 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:

MemberDescription
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​

<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>subscribable-jobs</artifactId>
<version>1.2.0</version>
</dependency>

API reference​

MemberDescription
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​

DependencyDescription
com.brightspot.job:jobProvides Job, JobRunner, and JobStatus, which the script job execution flow builds on.
com.brightspot.job:subscribable-jobsProvides SubscribableJob, which ScriptJob extends to support notifications.
com.brightspot.job:task-coreProvides RepeatingTaskUtils, used by AbstractAsyncProcessQueryScript (from cms-scripts-async).

Installation​

<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>cms-scripts-core</artifactId>
<version>1.2.0</version>
</dependency>

For a base script implementation that processes a Query result set asynchronously across multiple writer threads, also add:

<!-- Requires Brightspot 4.5 or later. -->
<dependency>
<groupId>com.brightspot.job</groupId>
<artifactId>cms-scripts-async</artifactId>
<version>1.2.0</version>
</dependency>

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.

MemberDescription
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.

MemberDescription
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.

MemberDescriptionDefault
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.

Was this page helpful?

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.