Job Module
A set of APIs to easily communicate with an asynchronous task |
Type | JAR |
Category | |
Developed by | |
Rating | |
License | GNU Lesser General Public License 2.1 |
Bundled With | XWiki Enterprise, XWiki Enterprise Manager |
Compatibility | Since 4.0 Milestone 1 |
Table of contents
Description
The goal of this module is to make easier to allow two ways communication with a task executed in a background thread.
Minimal job provide:
- progress information
- live log
- Java bean based question/answer system
and can be extended as long as the client of the API know the extended interface.
Use an existing job
The common way to use a job is through org.xwiki.job.JobExecutor component since 6.1 and org.xwiki.job.JobManager component before 6.1.
// Indicate an id to allow you to access the status of this unique task, the id is required to save/retrieve the status of the job
jobRequest.setId("taskid");
// Allow the job to ask questions during its executing (false by default)
jobRequest.setInteractive(true);
// [since 5.4] Indicate if the job should log informations about what is going on (to display it during the process for example, true by default)
jobRequest.setVerbose(true);
// [since 10.0] Force the serialization (or not) of the job status, null (the default) to fallback on {@link JobStatus#isSerialized()} (which is usually true)
jobRequest.setStatusSerialized(true);
// [since 10.0] Force the isolation (or not) of the job log from standard output, null (the default) to fallback on {@link JobStatus#isIsolated()} (which is usually true)
jobRequest.setStatusLogIsolated(true);
// Put some custom configuration
jobRequest.setProperty("some custom jobtype related parameter", "value");
// Lookup and add Job with role hint "jobtype" to the queue and return right away
Job job = jobExecutor.execute("jobtype", request);
// Wait until the job is finished
job.join();
Create a reusable job
To register a new job that anyone can then use in JobExecutor (or JobManager in older versions) you need to register a component with role org.xwiki.job.Job and a unique role hint.
Since 6.1 a Job can also implement org.xwiki.job.GroupedJob to indicate which group this job is part of. In a group jobs are executed in the same thread one by one by JobExecutor.
Since 7.4 a public org.xwiki.job.AbstractJob is provided to help implementing a Job. An org.xwiki.job.internal.AbstractJob class already existed since 5.0 but it was internal.
org.xwiki.job.AbstractJob is dealing with a lot of plumbing, among other things:
- it automatically store anything logged (the standard slf4j way) in the status
- it automatically listen to events generated trough org.xwiki.job.event.status.JobProgressManager component and update the progress in the job status
- it deals with question/answer complexity (waiting for an answer, etc.)
- it automatically store the job status asynchronously when the job is finished
- it log and generate related events when the job start and when the job ends
- it implements #join()
- it maintain a tree of jobs in the context
- since 10.2, it implements CancelableJobstatus
Execute custom job
JobExecutor provide an API to execute a custom instance of Job.
Starting with 5.1, it's also possible to create a custom Job and add it to the queue using JobManager#addJob(Job job).
Notify about progress
It's possible to notify about progression of a task by sending progression event that are then received by the job (or the jobs) to update its progress informations.
In Velocity scripts
[Since 7.1]
$services.progress.pushLevel(2)
// First step
$services.progress.startStep('First step')
#firstStep()
$services.progress.endStep()
// Second step
$services.progress.startStep('Second step')
#secondStep()
$services.progress.endStep()
$services.progress.popLevel()
In Java
Since 7.1
{
@Inject
private JobProgressManager progressManager;
void foo()
{
// Push 2 sub steps
progressManager.pushLevelProgress(2, this);
try {
// First step
this.progressManager.startStep(this, "First step");
firstStep();
this.progressManager.endStep(this);
// Second step
this.progressManager.startStep(this, "Second step");
secondStep();
} finally {
// Done, go back to parent progress level
this.progressManager.popLevelProgress(this);
}
}
void firstStep()
{
progressManager.call(new Callable() {
// First sub-step
progressManager.startStep(this);
firstSubStep();
progressManager.sendStep(this);
// Second sub-step
progressManager.startStep(this);
secondSubStep();
},
2, this);
}
}
Between 6.1 and 7.0:
{
@Inject
private JobProgressManager progressManager;
void foo()
{
// Push 2 sub steps
progressManager.pushLevelProgress(2, this);
try {
firstStep();
// First step done
this.progressManager.stepPropress(this);
secondStep();
} finally {
// Done, go back to parent progress level
this.progressManager.popLevelProgress(this);
}
}
void firstStep()
{
// Since 7.1M1 Callable support has been added
progressManager.call(new Callable() {
firstSubStep();
// First step done
progressManager.stepPropress(this);
secondSubStep();
},
2, this);
}
}
Before 6.1:
{
@Inject
private ObservationManager observationManager;
protected void notifyPushLevelProgress(int steps)
{
this.observationManager.notify(new PushLevelProgressEvent(steps), this);
}
protected void notifyStepPropress()
{
this.observationManager.notify(new StepProgressEvent(), this);
}
protected void notifyPopLevelProgress()
{
this.observationManager.notify(new PopLevelProgressEvent(), this);
}
void foo()
{
// Push 2 sub steps
notifyPushLevelProgress(2, this);
try {
firstStep();
// First step done
notifyStepPropress(this);
secondStep();
} finally {
// Done, go back to parent progress level
notifyPopLevelProgress(this);
}
}
}
Job status
Each job expose a live status which will also be automatically serialized when the job is finished (if the job has an id).
Progress
The job progress provide data to display live information about the job like a progress bar.
Since 7.1 it also provide a tree of steps with an associated message and elapsed time for each one.
Events
The following notification events are fired during the execution of a job whose status extends the default job status:
- JobStartedEvent: fired when the job starts. The event is send with the following parameters:
- source: the job that has started
- data: null
- QuestionAskedEvent: fired when an interactive job asks a question. The event listeners have the chance to answer the question before it reaches the user. The event is send with the following parameters:
- source: the status of the job that asked the question, which can be used to access the question
- data: null
- QuestionAnsweredEvent: Event fired after a question raised by an interactive job is answered. The event is send with the following parameters:
- source: the job status, that can be used to access the question
- data: null
- JobFinishedEvent: fired when the job finishes. The event is send with the following parameters:
- source: the job that has finished
- data: a Throwable if the job execution failed or null otherwise
- [since 8.1] JobFinishingEvent: fired just after a job is done and we start to save status etc. Give a chance to listeners to do action as part of the job execution before it's "closed". The event is send with the following parameters:
- source: the job that has finished
- data: a Throwable if the job execution failed or null otherwise
Script Service
Starting with XWiki 7.2M1 there is a script service available to retrieve:
- the status of a specified job
- the current job from a specified group and its status
#set ($farmDistributionJobStatus = $services.job.getJobStatus(['distribution']))
#set ($solrIndexerJobStatus = $services.job.getJobStatus(['solr', 'indexer']))
#set ($currentJobStatus = $services.job.getCurrentJobStatus(['my', 'job', 'group']))
The current job is $currentJobStatus.state
Job questions
See Job Question and Answer system.
Configuration
#-# The folder containing job executing status.
#-# The default is {environment.permanentDirectory}/jobs/
# job.statusFolder=/var/lib/xwiki/data/jobs/
#-# [Since 7.2M2]
#-# The maximum number of entries to put in the job status cache.
#-# The default is 50.
# job.statusCacheSize=50