|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||
@PublicApi public interface CompatibilityPluginScheduler
Provides a compatibility abstraction of scheduling cluster-safe tasks.
Plugins have historically either scheduled work through the PluginScheduler in the
Shared Access Layer or dealt with Quartz directly. The atlassian-scheduler library
is intended to replace both of these by hiding most of the complexity behind dealing with
scheduler systems, including making it easier for plugins to contend with lifecycle and
ClassLoader problems.
Plugins that do not need to run on older versions of Atlassian products should adopt the
atlassian-scheduler library directly as soon as the product does, but since many
plugin developers need to support older versions of the products as well, this is not
always possible.
This component provides a middle ground. The interface is closer in design to that of
atlassian-scheduler, but it will use SAL's PluginScheduler for its
implementation when atlassian-scheduler is not available. These details are
mostly hidden from the plugin developer; where differences in behaviour were unavoidable,
they are explicitly called out in the documentation. In particular:
JobRunner in atlassian-scheduler, the JobHandler in this
library must only be registered once during a plugin's lifecycle. It is a good idea to do
this as part of the plugin's initialization to make sure that it is registered if another
node in the cluster schedules a job for it.PluginScheduler, plugins should not unschedule their jobs when they are
destroyed, as this would remove them from the cluster completely. All they need to do is
unregister the JobHandler; this will unschedule the jobs implicitly if appropriate.PluginScheduler, plugin jobs might already exist when the plugin goes to
schedule them because they may have been created by another node of the cluster. See the
documentation for scheduleClusteredJob(String, JobHandlerKey, Date, long) for
advice on how to do deal with this safely.JobHandler or encoded into the job's key.atlassian-plugin.xml and will also need to import SAL's PluginScheduler:
<component key="compatibilityPluginScheduler" class="com.atlassian.scheduler.compat.AutoDetectingCompatibilityPluginScheduler" public="false">
<interface>com.atlassian.scheduler.compat.CompatibilityPluginScheduler</interface>
</component>
<component-import key="pluginScheduler" interface="com.atlassian.sal.api.scheduling.PluginScheduler" />
atlassian-scheduler-compat in its
pom.xml and have no dependencies on any other bundle from the com.atlassian.sheduler
group. Example:
<!-- Reference the compat library *only*; not the API! -->
<dependency>
<groupId>com.atlassian.scheduler</groupId>
<artifactId>atlassian-scheduler-compat</artifactId>
<version>${atlassian-scheduler.version}</version>
<scope>compile</scope>
</dependency>
com.atlassian.scheduler packages explicitly as optional
dependencies in the AMPS instructions section of pom.xml. Listing them as required dependencies
would prevent the plugin from loading in older versions, and failing to list them at all would result
in the plugin using the non-cluster-safe implementation even when the atlassian-scheduler library
is available. Example:
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>maven-APPLICATION-plugin</artifactId>
<version>${amps.version}</version>
<extensions>true</extensions>
<configuration>
<productVersion>${APPLICATION.version}</productVersion>
<productDataVersion>${APPLICATION.version}</productDataVersion>
<instructions>
<Import-Package>
com.atlassian.scheduler;resolution:="optional",
com.atlassian.scheduler.config;resolution:="optional",
com.atlassian.scheduler.status;resolution:="optional",
*
</Import-Package>
<Export-Package />
</instructions>
</configuration>
</plugin>
| Method Summary | |
|---|---|
JobInfo |
getJobInfo(String jobKey)
Returns information about a scheduled clustered job, if it exists. |
void |
registerJobHandler(JobHandlerKey jobHandlerKey,
JobHandler jobHandler)
Registers a job handler to accept jobs. |
void |
scheduleClusteredJob(String jobKey,
JobHandlerKey jobHandlerKey,
Date startTime,
long repeatInterval)
Schedule the given job to run once per cluster node. |
void |
unregisterJobHandler(JobHandlerKey jobHandlerKey)
Unregisters the specified job handler. |
void |
unscheduleClusteredJob(String jobKey)
Unschedule the given job for the entire cluster. |
| Method Detail |
|---|
void registerJobHandler(JobHandlerKey jobHandlerKey,
JobHandler jobHandler)
WARNING: Unlike the atlassian-scheduler, this compatibility library requires
that the job handler be registered exactly once before any jobs are scheduled. The compatibility
implementation that uses SAL's plugin scheduler binds the scheduled job to the implementation that was
registered at the time that it was scheduled. If there is no such implementation registered, then
there is nothing to associate the job with. If the implementation later changed, the job would already
bound with the old registration. To prevent inconsistent behaviour, multiple registration is explicitly
disallowed.
jobHandlerKey - the key that associates jobs with this job handlerjobHandler - the job handler which will process job requests
IllegalArgumentException - if the job handler key is already registeredvoid unregisterJobHandler(JobHandlerKey jobHandlerKey)
During cleanup, the plugin should call this for each job handler that it has registered.
Whether or not this automatically unschedules that job handler's jobs is
implementation-dependent, as it makes sense for the PluginScheduler implementation
to do this and does not make sense for the atlassian-scheduler implementation.
jobHandlerKey - the key for the job handler that will be unregistered.
void scheduleClusteredJob(String jobKey,
JobHandlerKey jobHandlerKey,
Date startTime,
long repeatInterval)
WARNING: it is very important not to try to call this method until the underlying application
is fully started. You should implement com.atlassian.sal.api.lifecycle.LifecycleAware and call
scheduleJob() only on com.atlassian.sal.api.lifecycle.LifecycleAware#onStart().
Only one node in the cluster needs to do this. If every node in the cluster schedules the same job to run immediately, then it will do so at the time each node schedules it (or shortly thereafter). For example, if you call this method unconditionally on start-up, then if two nodes are started 5 minutes apart, the job will run both times even if it is only supposed to happen once an hour.
To prevent this, plugin developers should be aware that a job could already be scheduled, either because it was scheduled by another cluster node or because it remained scheduled across a system restart. Write the scheduling call in a way that avoids changing the schedule when it already exists with the desired settings. Assuming that the schedule does not normally need to be updated, this will probably be as simple as:
if (compatibilityPluginScheduler.getJobInfo(JOB_KEY) != null)
{
compatibilityPluginScheduler.scheduleClusteredJob(JOB_KEY, JOB_HANDLER_KEY,
new Date(System.currentTimeMillis() + SCHEDULER_RACE_PADDING), INTERVAL);
}
Note the use of the scheduler race padding to offset the initial run time of the job. Using a value like
15000L (15 seconds) here provides a window where multiple nodes starting at once might all decide
to create the same job schedule. The padding helps ensure that this race condition has resolved by the
time the job runs for the first time, which guarantees that it only runs the expected number of times.
jobKey - A unique key for the jobjobHandlerKey - The lookup key for the job handler that will process the request. Unlike the
JobRunner in atlassian-scheduler, this must
be registered before the job
is scheduled.startTime - The time the job is to start.repeatInterval - How long of an interval to wait between repeated job executions, in milliseconds.
Note that implementations may restrict what values may be used here, and that it
is safest to use intervals that are a multiple of 60000L milliseconds (1 minute).
IllegalArgumentException - if the jobHandlerKey is not
registered.@Nullable JobInfo getJobInfo(String jobKey)
Note that this method should only be used if the compatibility layer would have been
used to register the job. Jobs that are scheduled directly with atlassian-scheduler
or PluginScheduler may or may not be known to this method depending on the implementation.
jobKey - the job's unique key
null if it is not scheduled.void unscheduleClusteredJob(String jobKey)
WARNING: This applies across the entire cluster. If the
plugin is just being disabled on one node as part of a graceful shutdown of the node,
then calling this method would remove the job from the other nodes as well, which is
probably the wrong thing to do. It will usually make more sense to call
unregisterJobHandler(JobHandlerKey), which will remove the jobs for you if
the pre-clustering implementation is used.
jobKey - The job key to unschedule
IllegalArgumentException - If the job doesn't exist thus cannot be unscheduled.
|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||