JBoss.orgCommunity Documentation
Scheduled jobs are simply components that execute on a possibly-recurring schedule instead of in response to user interaction. Scheduled jobs fire asynchronously, outside of the normal web-browser thread-of-control. Scheduled jobs have full access to the entire Ruby environment. This allows them to interact with database models and other application functionality.
Each scheduled job maps to exactly one Ruby class. The path and filename should match the class name of the job contained in the file.
| File name | Class name |
|---|---|
mail_notifier.rb | MailNotifier |
mail/notifier.rb | Mail::Notifier |
Example 10.1. Skeleton scheduled job class
(mail/notifier.rb)
module Mail
class Notifier
# implementation goes here
end
end
Each job class should implement a no-argument
run() method to perform the work when
fired. The class may optionally implement a one-argument
on_error(exception) method to handle
any errors raised during the job's execution.
Example 10.2. Scheduled job implementation
(mail/notifier.rb)
module Mail
class Notifier
# optional, only needed if you pass config options to the job
def initialize(options = {})
@options = options
end
def run()
# perform work here
end
def on_error(exception)
# Optionally implement this method to interrogate any exceptions
# raised inside the job's run method.
end
end
endFrom within the class's run() method, the
full application environment is available.
The job schedule defines the time(s) that a job should execute. This may be defined to be single point in time, or more often, as recurring event. The job schedule is defined in your deployment descriptor.
Within the internal torquebox.yml
descriptor (or through an external
descriptor), scheduled jobs are configured using the
*-knob.ymljobs: section, in which a block of
information is provided for each job. The block starts with an
arbitrary name for the job. Each block must also define the job
class and the schedule specification. Optionally a
timeout, a description, and a
config may be provided. Providing a
timeout will cause the job to be interrupted
if it runs beyond the timeout period (see Section 10.3.2, “Timing Out Jobs”).
If you provide
a config, its value will be passed to the
initialize method of the job class.
If you are using the DSL (via torquebox.rb) in
your internal descriptor, each job is defined using the job
directive, with very similar options to the YAML syntax described above.
The DSL does not require a name for each job, unless you intend to share
a job class across multiple jobs.
Example 10.3. Example deployment descriptor
Using the YAML syntax:
application:
..
jobs:
mail.notifier:
job: Mail::Notifier
cron: '0 */5 * * * ?'
timeout: 50000 ms
description: Deliver queued mail notifications
config:
foo: barAnd via the DSL:
TorqueBox.configure do
...
job Mail::Notifier do
name 'mail.notifier' # optional, unless the job class is used by multiple jobs
cron '0 */5 * * * ?'
timeout '5s'
description 'Deliver queued mail notifications' # optional
config do
foo 'bar'
end
end
endThe cron attribute should contain a typical crontab-like entry. It is composed of 7 fields (6 are required).
| Seconds | Minutes | Hours | Day of Month | Month | Day of Week | Year |
|---|---|---|---|---|---|---|
| 0-59 | 0-59 | 0-23 | 1-31 | 1-12 or JAN-DEC | 1-7 or SUN-SAT | 1970-2099 (optional) |
For several fields, you may denote subdivision by using the
forward-slash (/) character. To execute a job every 5
minutes, */5 in the minutes field would specify this
condition.
Spans may be indicated using the dash (-) character.
To execute a job Monday through Friday, MON-FRI should be
used in the day-of-week field.
Multiple values may be separated using the comma (,)
character. The specification of 1,15 in the day-of-month
field would result in the job firing on the 1st and 15th of each
month.
Either day-of-month or day-of-week must be specified using the
? character, since specifying both is contradictory.
To keep jobs from running too long, you can set a
timeout setting for the job. The format of
the timeout is a integer followed by an optional time unit.
The available time units are:
ms - milliseconds
s - seconds
m - minutes
h - hours
If no unit is provided, seconds are assumed.
In addition to specifying a timeout parameter, you will also have
to implement a on_timeout method on your job class
that will be called when the timeout occurs. This method is responsible
for actually shutting down the job - TorqueBox will not kill the job
when the timeout occurs. One approach would be for your job to periodically
check a flag while processing, with on_timeout setting
that flag when called.
Example 10.4. Job with timeout
(mail/notifier.rb)
module Mail
class Notifier
# optional, only needed if you pass config options to the job
def initialize(options = {})
@options = options
@timeout = false
end
def run()
notification_list.each do |n|
raise 'Timeout!' if @timeout
n.notify
end
end
def on_timeout
@timeout = true
end
end
endQuartz manages its own thread pool for running jobs. By default,
this pool contains three threads. If you have more than three jobs executing
at the same time, you may want to increase this pool size. You can do so via
the concurrency setting.
Example 10.5. Setting job concurrency
Using the YAML syntax:
application:
..
jobs:
concurrency: 10
mail.notifier:
job: Mail::Notifier
cron: '0 */5 * * * ?'And via the DSL:
TorqueBox.configure do
...
options_for :jobs, :concurrency => 10
job Mail::Notifier do
name 'mail.notifier' # optional, unless the job class is used by multiple jobs
cron '0 */5 * * * ?'
end
endNote that if you are using a bounded runtime pool for the jobs subsystem that is smaller than the concurrency setting, your available concurrency will be limited to the pool size. See Chapter 16, TorqueBox Runtime Pooling for more details.
In addition to creating jobs defined in the deployment descriptors you can create and remove them at runtime too.
It is possible to create a new job at runtime. You need to use the
schedule method available in the
TorqueBox::ScheduledJob module. This method
returns true if the task is completed or
false otherwise. There is a default
timeout set to 30 seconds meaning that if the job will not be scheduled in the
mentioned time the method will finish immediately returning false.
Example 10.6. Scheduling a job
TorqueBox::ScheduledJob.schedule('JobClassName', '*/10 * * * * ?')
This simple execution will create a new scheduled job implemented
in the JobClassName class and run it every
10 seconds.
TorqueBox::ScheduledJob.schedule('JobClassName', '*/10 * * * * ?',
:name => 'simple.job',
:description => 'This is a simple job',
:timeout => '2s',
:config => {"number" => 1, "text" => "bacon"},
:singleton => false)This example shows all the available options. Please see the table below for explanation.
The schedule method is executed asynchronously and returns
a java.util.concurrent.CountDownLatch object which can be used
to wait for the task completion. If you want to have a synchronous method
use the schedule_sync method. It will block and return
true after successful task completion and
false otherwise.
The job class name and cron expression is required. Additionally
the schedule method accepts following,
optional parameters:
Table 10.1. Job scheduling options
| Option | Default | Description |
|---|---|---|
:name | "default" | The job name unique across the application. |
:description | Job description. | |
:timeout | "0s" | The time after the job execution should be interrupted. By default it'll never interrupt the job execution. |
:config | Data that should be injected to the job constructor. | |
:singleton | true | Flag to determine if the job should be executed on every node in the cluster or only on one node (default). |
Every job requires a unique name across the application. By default,
if there is no :name parameter provided the name
will be set to the class name. In case the job class name includes module
name, like this: Module::ClassName, the job name will
be set to Module.ClassName.
If you schedule a job with a name of a job already deployed - the old job will be replaced with the new one.
Note that if you schedule a job at runtime it'll not be persisted and is lost after the server restart.
You can easily remove a scheduled job. To do this use the
remove method available in the
TorqueBox::ScheduledJob module. This method
returns true if the task is completed or
false otherwise. There is a default
timeout set to 30 seconds meaning that if the job will not be removed in the
mentioned time the method will finish immediately returning false.
The remove method is executed asynchronously and returns
a java.util.concurrent.CountDownLatch object which can be used
to wait for the task completion. If you want to have a synchronous method
use the remove_sync method. It will block and return
true after successful task completion and
false otherwise.
Example 10.7. Removing a job
TorqueBox::ScheduledJob.remove('simple.job')This example will lookup a job with the 'simple.job' name and remove it.
Note that if you remove a job defined in the deployment descriptor, it'll be started again after server restart.
'At' jobs are jobs that use different scheduling mechanism compared to regular scheduled jobs where you define the execution time with cron expressions. In case of 'at' jobs you have more control over the execution of the job.
Example 10.8. Examples of scheduling 'at' jobs
# The job will be executed every 200 ms, from now, for the next 10 seconds
TorqueBox::ScheduledJob.at('SimpleJob', :every => 200, :until => Time.now + 10)
# The job will be executed for the first time in 10 seconds (current time + 10 seconds), then every
# 500 ms, for the next 10 seconds (current time + 20 seconds)
TorqueBox::ScheduledJob.at('SimpleJob', :at => Time.now + 10, :every => 500, :until => Time.now + 20)
# The job will be executed for the first time in 5s and repeated 3 times
# The time between executions is set to 1.5s
TorqueBox::ScheduledJob.at('SimpleJob', :in => 5000, :repeat => 3, :every => 1500)
The at method is executed asynchronously and returns
a java.util.concurrent.CountDownLatch object which can be used
to wait for the task completion. If you want to have a synchronous method
use the at_sync method. It will block and return
true after successful task completion and
false otherwise.
The first parameter of the at method is the class
name of the job implementation to execute. The second parameter
allows to specify when the job should be executed.
Below you can find valid options.
Table 10.2. 'At' job scheduling options
| Option | Default | Description |
|---|---|---|
:at | Time.now |
Specifies when the at job should start firing. Must be a
Time class. Can't be specified with
:in.
|
:in | Specifies when the at job should start firing, in ms from now. Can't be specified with :at. | |
:every | Specifies the delay interval between at job firings, in ms. If specified without a :repeat or :until, the job will fire indefinitely. | |
:repeat | Specifies the number of times an at job should repeat beyond its initial firing. Requires :every to be provided. | |
:until | Specifies when the at job should stop firing. Must be a Time class. | |
:name | job class name | The job name unique across the application. |
:description | Job description. | |
:timeout | "0s" | The time after the job execution should be interrupted. By default it'll never interrupt the job execution. |
:config | Data that should be injected to the job constructor. | |
:singleton | true | Flag to determine if the job should be executed on every node in the cluster or only on one node (default). |
TorqueBox supports highly-available singleton jobs. By default, a job only runs on one node in the cluster and if that node goes down or the job fails to run to completion, it is automatically scheduled on a new node.
To use high availability singleton jobs, you must start TorqueBox with a clustered configuration. For example:
$$JBOSS_HOME/bin/standalone.sh --server-config=standalone-ha.xml
Alternatively, use the torquebox command:
$torquebox run --clustered
HA jobs are configured using the singleton
key in the job specification in your deployment descriptor. Its
default value is true so you must manually
configure it with a value of false for the job to
run on every node in the cluster.
Example 10.9. Example deployment descriptor
Using the YAML syntax:
application:
..
jobs:
mail.notifier:
job: Mail::Notifier
cron: '0 */5 * * * ?'
description: Deliver queued mail notifications
singleton: true
config:
foo: barAnd via the DSL:
TorqueBox.configure do
...
job Mail::Notifier do
name 'mail.notifier' # optional, unless the job class is used by multiple jobs
cron '0 */5 * * * ?'
description 'Deliver queued mail notifications' # optional
singleton true
config do
foo 'bar'
end
end
endThis is the same
deployment descriptor from the example above.
Including the singleton attribute with a value
of true is redundant of course, since jobs will
only run on a single node when clustered, by default.
If a job requires access to other resources, such as messaging topics and queues, or Java CDI components these should be injected using the resource injection facilities provided by TorqueBox (see Chapter 12, TorqueBox Resource Injection).