View Javadoc

1   package com.atlassian.scheduler;
2   
3   import com.atlassian.annotations.PublicApi;
4   import com.atlassian.scheduler.config.JobConfig;
5   import com.atlassian.scheduler.config.JobId;
6   import com.atlassian.scheduler.config.JobRunnerKey;
7   import com.atlassian.scheduler.config.Schedule;
8   import com.atlassian.scheduler.cron.CronExpressionValidator;
9   import com.atlassian.scheduler.status.JobDetails;
10  import com.atlassian.scheduler.status.RunOutcome;
11  
12  import javax.annotation.CheckForNull;
13  import javax.annotation.Nonnull;
14  import javax.annotation.Nullable;
15  import java.util.Date;
16  import java.util.List;
17  import java.util.Set;
18  
19  /**
20   * Scheduler service for Atlassian products and their plugins.
21   * <p>
22   * This service provides the ability to schedule services for execution, either as a one time task,
23   * as a repeating task, or according to a formal {@code cron}-style schedule.
24   * </p><p>
25   * Applications and add-ons define the scheduled work to perform by registering a {@link JobRunner}
26   * with a {@link JobRunnerKey unique key}.  The scheduled work is performed according to individual
27   * {@link #scheduleJob(JobId, JobConfig) scheduled jobs} that the define its {@link JobConfig configuration},
28   * including {@link Schedule when it should run} and any {@link JobConfig#getParameters() parameters}
29   * that the job runner needs to know
30   * The work to be performed is registered as a {@link JobRunner}, and the schedule and associated
31   * data are set by scheduling a specific job for that runner.  Multiple jobs can be
32   * {@link #scheduleJob(JobId, JobConfig) scheduled} for a given job runner.
33   * </p>
34   */
35  @PublicApi
36  public interface SchedulerService {
37      /**
38       * Registers the job runner for a given job runner key.  Registration does not survive application restart,
39       * and must be done on each node for clustered applications.  A second registration with the same
40       * {@code jobRunnerKey} will replace any existing registration.
41       * <p>
42       * A job that is scheduled to run but has no registered job runner is reported as
43       * {@link RunOutcome#UNAVAILABLE unavailable}.
44       * </p>
45       *
46       * @param jobRunnerKey Globally unique job runner key.
47       * @param jobRunner    the concrete object capable of running instances of this job
48       */
49      void registerJobRunner(JobRunnerKey jobRunnerKey, JobRunner jobRunner);
50  
51      /**
52       * Unregisters the specified job runner.  Plugins should unregister their job runners as part of
53       * being disabled.
54       * <p>
55       * Jobs that fire with no registered job runner will fail to start.
56       * </p>
57       *
58       * @param jobRunnerKey Globally unique job runner key.
59       */
60      void unregisterJobRunner(JobRunnerKey jobRunnerKey);
61  
62      /**
63       * Returns all of the job runner keys that currently have registered job runners, regardless
64       * of whether or not any jobs have actually been {@link #scheduleJob(JobId, JobConfig) scheduled}
65       * for them.  The job runner keys are not guaranteed to be returned in any particular order.
66       *
67       * @return an immutable set containing all of the registered job runner keys
68       * @see #getJobRunnerKeysForAllScheduledJobs()
69       */
70      @Nonnull
71      Set<JobRunnerKey> getRegisteredJobRunnerKeys();
72  
73      /**
74       * Returns all of the job runner keys that have been used to schedule jobs, regardless
75       * of whether or not {@link JobRunner}s are currently registered for them.  The job
76       * runner keys are not guaranteed to be returned in any particular order.
77       *
78       * @return an immutable set containing all of the job runner keys with scheduled jobs
79       * @see #getRegisteredJobRunnerKeys()
80       */
81      @Nonnull
82      Set<JobRunnerKey> getJobRunnerKeysForAllScheduledJobs();
83  
84  
85      /**
86       * Schedules a job with the given job ID.
87       * <p>
88       * If a job already exists with the given ID, then it will be replaced with the new run config.
89       * If the schedule is eligible to run immediately and multiple nodes take this action at close
90       * to the same time, then the job might run more than once as the instances replace one
91       * another.
92       * </p>
93       * <p>
94       * In most cases, this will be harmless, but it can be avoided by making sure the job will not be
95       * eligible to run until some time in the future.  For example, when using an interval schedule,
96       * the caller can first check whether or not the job already exists, and if it does not then
97       * specify an initial start date for the schedule, as in:
98       * </p>
99       * <pre><code>
100      *     Schedule.forInterval(120000L, new Date(System.currentTimeMillis() + 15000L))
101      * </code></pre>
102      * <p>
103      * Since the schedule will not be eligible to run until 15 seconds after the current time,
104      * any race conditions between two nodes starting up at once and trying to schedule the same
105      * job should resolve before the job actually fires.  For cron expressions, this is a little
106      * bit more difficult, but you can set the seconds field to an explicit value to accomplish
107      * the same thing.  For example:
108      * </p>
109      * <pre><code>
110      *     final Calendar calendar = new GregorianCalendar();
111      *     calendar.add(15, Calendar.SECOND);
112      *     final Schedule schedule = Schedule.forCronExpression(
113      *             calendar.get(Calendar.SECOND) + " 0 2 * * ?");  // at or just after 2 A.M.
114      *     scheduleJob(...
115      * </code></pre>
116      *
117      * @param jobId     the Job ID
118      * @param jobConfig the configuration details for the job instance including schedule,
119      *                  run mode, run parameters, etc.
120      * @throws SchedulerServiceException if the job cannot be scheduled because there is a problem
121      *                                   with either the provided configuration or within the scheduler implementation itself
122      */
123     void scheduleJob(JobId jobId, JobConfig jobConfig) throws SchedulerServiceException;
124 
125     /**
126      * Schedules a "dynamic" job by generating a new unique job ID.
127      * <p>
128      * This method should normally only be used when creating multiple jobs for a given job runner key
129      * that need to run independently &mdash; most likely because these are created in response to user
130      * input.
131      * </p>
132      *
133      * @param jobConfig the configuration details for the job instance including schedule,
134      *                  run mode, run parameters, etc.
135      * @return the generated unique Job ID
136      * @throws SchedulerServiceException if the job cannot be scheduled because there is a problem
137      *                                   with either the provided configuration or within the scheduler implementation itself
138      */
139     @Nonnull
140     JobId scheduleJobWithGeneratedId(JobConfig jobConfig) throws SchedulerServiceException;
141 
142     /**
143      * Unschedules a previously scheduled job ID.
144      * <p>
145      * If no such job exists, then the request is ignored.
146      * </p>
147      *
148      * @param jobId the Job ID to be unregistered
149      */
150     void unscheduleJob(JobId jobId);
151 
152     /**
153      * Returns the next time that a job with the given schedule would be expected to run.
154      * <p>
155      * Caveats:
156      * </p>
157      * <ul>
158      * <li>Interval schedules taken from {@link JobDetails} are not aware of the job that they came from
159      * or whether or not that job has previously run.  They are calculated on the basis of the current
160      * time, not the original job's history.</li>
161      * <li>Schedules based on {@link Schedule#forCronExpression(String) cron expressions} are implicitly
162      * {@link CronExpressionValidator#validate(String) validated} by this request.</li>
163      * <li>The initial run time reported is not a strong guarantee.  The actual initial run time that is
164      * calculated by the schedule may depend on the exact time that the schedule is created.  The
165      * return value should be treated as an estimate, only.</li>
166      * </ul>
167      *
168      * @param schedule the schedule to evaluate
169      * @return the estimated time that the schedule would next run, or {@code null} if it would never run
170      * @throws SchedulerServiceException if {@code schedule} is invalid.
171      * @since v1.6.0
172      */
173     @Nullable
174     Date calculateNextRunTime(Schedule schedule) throws SchedulerServiceException;
175 
176     /**
177      * Retrieves the details for the specified job ID.
178      *
179      * @param jobId the Job ID for which to retrieve the details
180      * @return the job details, or {@code null} if no such job is defined
181      */
182     @CheckForNull
183     JobDetails getJobDetails(JobId jobId);
184 
185     /**
186      * Retrieves the job details for all jobs with the given job runner key.
187      *
188      * @param jobRunnerKey the job runner key to look up
189      * @return the jobs that are registered with the given job runner key, or an empty
190      * collection if there are no jobs that use the given key; never {@code null}
191      */
192     @Nonnull
193     List<JobDetails> getJobsByJobRunnerKey(JobRunnerKey jobRunnerKey);
194 }
195