View Javadoc

1   package com.atlassian.scheduler.config;
2   
3   import com.atlassian.annotations.PublicApi;
4   import com.atlassian.scheduler.SchedulerService;
5   import com.atlassian.scheduler.status.RunOutcome;
6   import com.google.common.base.Predicates;
7   
8   import javax.annotation.Nullable;
9   import javax.annotation.concurrent.Immutable;
10  import java.util.Date;
11  import java.util.Objects;
12  import java.util.TimeZone;
13  
14  import static com.atlassian.util.concurrent.Assertions.isTrue;
15  import static com.atlassian.util.concurrent.Assertions.notNull;
16  import static com.google.common.collect.Iterables.filter;
17  import static com.google.common.collect.Iterables.size;
18  import static java.util.Arrays.asList;
19  
20  /**
21   * Represents a schedule used to run a job at particular times.  Instances of this
22   * class are not created directly.  Use one of the factory methods — such as
23   * {@link #forInterval(long, Date)} or {@link #forCronExpression(String, TimeZone)} —
24   * to construct them.  To recover the information, use {@link #getType()} to identify the
25   * type of schedule information and call the appropriate getter ({@link #getIntervalScheduleInfo()}
26   * for {@link Type#INTERVAL}; {@link #getCronScheduleInfo()} for {@link Type#CRON_EXPRESSION}).
27   */
28  @Immutable
29  @PublicApi
30  public final class Schedule {
31      /**
32       * Creates a new schedule for the given cron expression.  The cron expression is not verified immediately,
33       * but invalid expressions will cause {@link SchedulerService#scheduleJobWithGeneratedId(JobConfig)} to fail.  The system's
34       * {@link TimeZone#getDefault() default time zone} is assumed.
35       *
36       * @param cronExpression the cron expression to use for the schedule
37       * @return a schedule for running jobs when the given cron expression is satisfied
38       */
39      public static Schedule forCronExpression(String cronExpression) {
40          return forCronExpression(cronExpression, null);
41      }
42  
43      /**
44       * Creates a new schedule for the given cron expression.  The cron expression is not verified immediately,
45       * but invalid expressions will cause {@link SchedulerService#scheduleJobWithGeneratedId(JobConfig)} to fail.
46       *
47       * @param cronExpression the cron expression to use for the schedule
48       * @param timeZone       the time zone within which to apply the rules of the cron expression; may be {@code null}
49       *                       to use a default time zone that is appropriate for the application
50       * @return a schedule for running jobs when the given cron expression is satisfied
51       */
52      public static Schedule forCronExpression(String cronExpression, @Nullable TimeZone timeZone) {
53          return new Schedule(Type.CRON_EXPRESSION, null, new CronScheduleInfo(cronExpression, timeZone));
54      }
55  
56      /**
57       * Creates a new schedule that will run once at the specified time.  Jobs that are scheduled to run once
58       * are not guaranteed to remain in the system after they are attempted, regardless of the {@link RunOutcome}.
59       * Jobs scheduled as {@code runOnce} will be purged automatically after they have run; that is, at some
60       * point after they have run (possibly immediately), {@link SchedulerService#getJobDetails(JobId)} will
61       * no longer return the job's information.
62       *
63       * @param runTime when the job should run; may be {@code null} to indicate that the job should run
64       *                as soon as possible
65       * @return a schedule for running once at the given time
66       */
67      public static Schedule runOnce(@Nullable Date runTime) {
68          return forInterval(0L, runTime);
69      }
70  
71      /**
72       * Creates a new schedule that will run periodically with the given interval.
73       * <p>
74       * <strong>WARNING</strong>: Implementations are not required to honour the time interval at millisecond
75       * precision.  The actual resolution that is permitted is implementation-defined.  Implementations should
76       * round the requested interval up and persist the rounded value so that the interval reported by
77       * later calls to {@link Schedule#getIntervalScheduleInfo()} will be consistent with the behaviour that the
78       * scheduler actually provides.
79       * </p>
80       *
81       * @param intervalInMillis the minimum time interval (in milliseconds) between runs.  If the
82       *                         value {@code 0L} is specified, then the job will not repeat (this is equivalent to using
83       *                         {@link #runOnce(Date) runOnce(firstRunTime)}).  Negative values are not permitted.
84       * @param firstRunTime     when the job should run for the first time; may be {@code null} to indicate that
85       *                         the job should run as soon as possible
86       * @return a schedule for running once at the given time
87       */
88      public static Schedule forInterval(long intervalInMillis, @Nullable Date firstRunTime) {
89          return new Schedule(Type.INTERVAL,
90                  new IntervalScheduleInfo(firstRunTime, intervalInMillis),
91                  null);
92      }
93  
94  
95      private final Type type;
96      private final IntervalScheduleInfo intervalScheduleInfo;
97      private final CronScheduleInfo cronScheduleInfo;
98  
99      private Schedule(final Type type, @Nullable final IntervalScheduleInfo intervalScheduleInfo,
100                      @Nullable final CronScheduleInfo cronScheduleInfo) {
101         this.type = notNull("type", type);
102         this.intervalScheduleInfo = intervalScheduleInfo;
103         this.cronScheduleInfo = cronScheduleInfo;
104 
105         isTrue("Exactly one of the schedule formats must be non-null",
106                 countNulls(intervalScheduleInfo, cronScheduleInfo) == 1);
107     }
108 
109     private static int countNulls(Object... schedules) {
110         return size(filter(asList(schedules), Predicates.notNull()));
111     }
112 
113 
114     /**
115      * Returns a representation of the simple settings that were used to create this schedule.
116      *
117      * @return the interval schedule, or {@code null} if that is not this schedule's {@link #getType() type}.
118      */
119     public IntervalScheduleInfo getIntervalScheduleInfo() {
120         return intervalScheduleInfo;
121     }
122 
123     /**
124      * Returns a representation of the cron settings that were used to create this schedule.
125      *
126      * @return the cron schedule, or {@code null} if that is not this schedule's {@link #getType() type}.
127      */
128     public CronScheduleInfo getCronScheduleInfo() {
129         return cronScheduleInfo;
130     }
131 
132     /**
133      * Returns the {@link Type} of this schedule.
134      *
135      * @return the {@link Type} of this schedule.
136      */
137     public Type getType() {
138         return type;
139     }
140 
141 
142     @Override
143     public boolean equals(@Nullable final Object o) {
144         if (this == o) {
145             return true;
146         }
147         if (o == null || getClass() != o.getClass()) {
148             return false;
149         }
150 
151         final Schedule other = (Schedule) o;
152         return type == other.type
153                 && Objects.equals(intervalScheduleInfo, other.intervalScheduleInfo)
154                 && Objects.equals(cronScheduleInfo, other.cronScheduleInfo);
155     }
156 
157     @Override
158     public int hashCode() {
159         return Objects.hash(type, intervalScheduleInfo, cronScheduleInfo);
160     }
161 
162     @Override
163     public String toString() {
164         final StringBuilder sb = new StringBuilder(128).append("Schedule[type=").append(type);
165         switch (type) {
166             case CRON_EXPRESSION:
167                 sb.append(",cronScheduleInfo=").append(cronScheduleInfo);
168                 break;
169             case INTERVAL:
170                 sb.append(",intervalScheduleInfo=").append(intervalScheduleInfo);
171                 break;
172         }
173         return sb.append(']').toString();
174     }
175 
176 
177     public static enum Type {
178         /**
179          * A schedule that uses a cron expression to control when the job runs.
180          *
181          * @see CronScheduleInfo
182          * @see #getCronScheduleInfo()
183          */
184         CRON_EXPRESSION,
185 
186         /**
187          * A schedule that uses a interval to control when the job runs.
188          *
189          * @see IntervalScheduleInfo
190          * @see #getIntervalScheduleInfo()
191          */
192         INTERVAL
193     }
194 }
195