public class TenantAwareSchedulerService extends DelegatingSchedulerService implements LifecycleAwareSchedulerService
Any attempt to schedule a job with no tenant available results in an exception.
Implemention note: This provides the full LifecycleAwareSchedulerService interface
rather than the SchedulerService subset so that the raw, non-tenant-aware delegate
can be completely hidden away at construction time without losing the ability to control
its lifecycle. For example, in JIRA (which uses PicoContainer, instead of registering
a FooSchedulerService directly as the LifecycleAwareSchedulerService
implementation, we might register something like this instead:
// Registrations:
// FooConfiguration => JiraFooConfiguration
// RunDetailsDao => OfbizRunDetailsDao
// TenantAccessor => DefaultJiraTenantAccessor
public class JiraFooSchedulerService extends TenantAwareSchedulerService
{
public JiraFooSchedulerService(FooConfiguration config, RunDetailsDao runDetailsDao,
TenantAccessor tenantAccessor)
{
super(new FooSchedulerService(config, runDetailsDao), tenantAccessor);
}
}
It is not, of course, absolutely necessary to do this. We could register FooSchedulerService directly
and use an explicit constructor specification for TenantAwareSchedulerService that will use
FooSchedulerService as the parameter's key instead of the LifecycleAwareSchedulerService
key that Pico would find by reflection. However, there doesn't seem to be any real benefit to the approach
unless there are legitimate reasons to access the FooSchedulerService directly, which would bypass
the tenant-awareness.
LifecycleAwareSchedulerService.State| Constructor and Description |
|---|
TenantAwareSchedulerService(LifecycleAwareSchedulerService delegate,
com.atlassian.tenancy.api.TenantAccessor tenantAccessor) |
| Modifier and Type | Method and Description |
|---|---|
Collection<RunningJob> |
getLocallyRunningJobs()
Returns the list of jobs that are currently executing on this node.
|
LifecycleAwareSchedulerService.State |
getState()
Returns the scheduler service's running state.
|
void |
scheduleJob(JobId jobId,
JobConfig jobConfig)
Schedules a job with the given job ID.
|
JobId |
scheduleJobWithGeneratedId(JobConfig jobConfig)
Schedules a "dynamic" job by generating a new unique job ID.
|
void |
shutdown()
Permanent shutdown of the scheduler.
|
void |
standby()
Places the scheduler into standby mode.
|
void |
start()
Starts the scheduler if it had never been started or had been placed in
SchedulerServiceController.standby() mode. |
boolean |
waitUntilIdle(long timeout,
TimeUnit units)
Waits for up to
timeout units for any currently executing jobs to complete. |
calculateNextRunTime, getJobDetails, getJobRunnerKeysForAllScheduledJobs, getJobsByJobRunnerKey, getRegisteredJobRunnerKeys, registerJobRunner, unregisterJobRunner, unscheduleJobclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitcalculateNextRunTime, getJobDetails, getJobRunnerKeysForAllScheduledJobs, getJobsByJobRunnerKey, getRegisteredJobRunnerKeys, registerJobRunner, unregisterJobRunner, unscheduleJobpublic TenantAwareSchedulerService(LifecycleAwareSchedulerService delegate, com.atlassian.tenancy.api.TenantAccessor tenantAccessor)
public void scheduleJob(JobId jobId, JobConfig jobConfig) throws SchedulerServiceException
SchedulerServiceIf a job already exists with the given ID, then it will be replaced with the new run config. If the schedule is eligible to run immediately and multiple nodes take this action at close to the same time, then the job might run more than once as the instances replace one another.
In most cases, this will be harmless, but it can be avoided by making sure the job will not be eligible to run until some time in the future. For example, when using an interval schedule, the caller can first check whether or not the job already exists, and if it does not then specify an initial start date for the schedule, as in:
Schedule.forInterval(120000L, new Date(System.currentTimeMillis() + 15000L))
Since the schedule will not be eligible to run until 15 seconds after the current time, any race conditions between two nodes starting up at once and trying to schedule the same job should resolve before the job actually fires. For cron expressions, this is a little bit more difficult, but you can set the seconds field to an explicit value to accomplish the same thing. For example:
final Calendar calendar = new GregorianCalendar();
calendar.add(15, Calendar.SECOND);
final Schedule schedule = Schedule.forCronExpression(
calendar.get(Calendar.SECOND) + " 0 2 * * ?"); // at or just after 2 A.M.
scheduleJob(...
scheduleJob in interface SchedulerServicescheduleJob in class DelegatingSchedulerServicejobId - the Job IDjobConfig - the configuration details for the job instance including schedule,
run mode, run parameters, etc.SchedulerServiceException - if the job cannot be scheduled because there is a problem
with either the provided configuration or within the scheduler implementation itself@Nonnull public JobId scheduleJobWithGeneratedId(JobConfig jobConfig) throws SchedulerServiceException
SchedulerServiceThis method should normally only be used when creating multiple jobs for a given job runner key that need to run independently — most likely because these are created in response to user input.
scheduleJobWithGeneratedId in interface SchedulerServicescheduleJobWithGeneratedId in class DelegatingSchedulerServicejobConfig - the configuration details for the job instance including schedule,
run mode, run parameters, etc.SchedulerServiceException - if the job cannot be scheduled because there is a problem
with either the provided configuration or within the scheduler implementation itselfpublic void start()
throws SchedulerServiceException
SchedulerServiceControllerSchedulerServiceController.standby() mode.
If the scheduler was already active, then the request has no effect.start in interface SchedulerServiceControllerSchedulerServiceException - if the scheduler cannot be startedpublic void standby()
throws SchedulerServiceException
SchedulerServiceControllerstarted
again. If the scheduler was already in standby, then the request has no effect. The standby mode only
affects the local node's ability to schedule jobs. For any
RunMode.RUN_ONCE_PER_CLUSTER jobs the jobs may still run on other
nodes if they exist and have started. If a job should have run while the scheduler was in standby mode, the
implementation may trigger those jobs when restarted, but this is not guaranteed.standby in interface SchedulerServiceControllerSchedulerServiceException - if the scheduler cannot be be placed in standby modepublic void shutdown()
SchedulerServiceControllershutdown in interface SchedulerServiceController@Nonnull public Collection<RunningJob> getLocallyRunningJobs()
SchedulerServiceControllerSchedulerServiceController.shutdown().getLocallyRunningJobs in interface SchedulerServiceControllerpublic boolean waitUntilIdle(long timeout,
TimeUnit units)
throws InterruptedException
SchedulerServiceControllertimeout units for any currently executing jobs to complete.
Note that if the scheduler has not been SchedulerServiceController.shutdown() or placed in SchedulerServiceController.standby()
mode, then jobs could start after this returns true. As with SchedulerServiceController.getLocallyRunningJobs(),
this is only aware of jobs running on this node of the cluster, and it is guaranteed to be safe to
call even after a SchedulerServiceController.shutdown().waitUntilIdle in interface SchedulerServiceControllertimeout - the timeout period, in the specified units; non-positive values request an immediate
poll — that is, it is equivalent to getLocallyRunningJobs().isEmpty()true if the scheduler is now idle; false if jobs are still executing.InterruptedException - if the current thread is interrupted while waiting for the
scheduler to become idle.@Nonnull public LifecycleAwareSchedulerService.State getState()
SchedulerServiceControllerLifecycleAwareSchedulerService.State.STANDBY
and can be moved between this state and LifecycleAwareSchedulerService.State.STANDBY freely. LifecycleAwareSchedulerService.State.SHUTDOWN is
terminal — that is, once shut down, the scheduler's state can no longer be changed.getState in interface SchedulerServiceControllerCopyright © 2017 Atlassian. All rights reserved.