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 — 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