View Javadoc

1   package org.apache.onami.scheduler;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static java.lang.String.format;
23  import static java.util.TimeZone.getDefault;
24  import static org.apache.onami.scheduler.Scheduled.DEFAULT;
25  import static org.quartz.CronScheduleBuilder.cronSchedule;
26  import static org.quartz.JobBuilder.newJob;
27  import static org.quartz.JobKey.jobKey;
28  import static org.quartz.TriggerBuilder.newTrigger;
29  import static org.quartz.TriggerKey.triggerKey;
30  import static org.quartz.utils.Key.DEFAULT_GROUP;
31  
32  import java.util.TimeZone;
33  
34  import javax.inject.Inject;
35  
36  import org.quartz.Job;
37  import org.quartz.JobKey;
38  import org.quartz.Scheduler;
39  import org.quartz.Trigger;
40  import org.quartz.TriggerKey;
41  
42  import com.google.inject.ProvisionException;
43  
44  /**
45   * DSL to produce {@code Job} and add to a {@code Scheduler},
46   * and associate the related {@code Trigger} with it.
47   */
48  public final class JobSchedulerBuilder {
49  
50      /**
51       * The type of the {@code Job} to be executed.
52       */
53      private final Class<? extends Job> jobClass;
54  
55      /**
56       * The {@code Job} name, must be unique within the group.
57       */
58      private String jobName = DEFAULT;
59  
60      /**
61       * The {@code Job} group name.
62       */
63      private String jobGroup = DEFAULT_GROUP;
64  
65      /**
66       * Instructs the {@code Scheduler} whether or not the {@code Job} should
67       * be re-executed if a {@code recovery} or {@code fail-over} situation is
68       * encountered.
69       */
70      private boolean requestRecovery = false;
71  
72      /**
73       * Whether or not the {@code Job} should remain stored after it is
74       * orphaned (no {@code Trigger}s point to it).
75       */
76      private boolean storeDurably = false;
77  
78      /**
79       * The {@code Trigger} name, must be unique within the group.
80       */
81      private String triggerName = DEFAULT;
82  
83      /**
84       * The {@code Trigger} group.
85       */
86      private String triggerGroup = DEFAULT_GROUP;
87  
88      /**
89       * The cron expression to base the schedule on.
90       */
91      private String cronExpression;
92  
93      /**
94       * The time zone for which the {@code cronExpression}
95       * of this {@code CronTrigger} will be resolved.
96       */
97      private TimeZone timeZone = getDefault();
98  
99      /**
100      * The {@code Trigger}'s priority.  When more than one {@code Trigger} have the same
101      * fire time, the scheduler will fire the one with the highest priority
102      * first.
103      */
104     private int priority = 0;
105 
106     /**
107      * The {@code Trigger} to be used to schedule the {@code Job}
108      *
109      * @since 1.2
110      */
111     private Trigger trigger;
112 
113     /**
114      * Indicates whether the job's trigger should be updated if it is already existing when being
115      * scheduled (which is typically the case with a persistent {@link org.quartz.spi.JobStore}.
116      *
117      * @since 1.3
118      */
119     private boolean updateExistingTrigger = false;
120 
121     /**
122      * Creates a new {@code JobSchedulerBuilder} instance.
123      *
124      * This class can't be instantiated by users.
125      *
126      * @param jobClass The type of the {@code Job} to be executed
127      */
128     JobSchedulerBuilder( final Class<? extends Job> jobClass )
129     {
130         this.jobClass = jobClass;
131     }
132 
133     /**
134      * Sets the {@code Job} name, must be unique within the group.
135      *
136      * @param jobName The {@code Job} name, must be unique within the group
137      * @return This builder instance
138      */
139     public JobSchedulerBuilder withJobName( String jobName )
140     {
141         this.jobName = jobName;
142         return this;
143     }
144 
145     /**
146      * Sets the {@code Job} group.
147      *
148      * @param jobGroup The {@code Job} group
149      * @return This builder instance
150      */
151     public JobSchedulerBuilder withJobGroup( String jobGroup )
152     {
153         this.jobGroup = jobGroup;
154         return this;
155     }
156 
157     /**
158      * Instructs the {@code Scheduler} whether or not the {@code Job} should
159      * be re-executed if a {@code recovery} or {@code fail-over} situation is
160      * encountered.
161      *
162      * @param requestRecovery The activation flag
163      * @return This builder instance
164      */
165     public JobSchedulerBuilder withRequestRecovery( boolean requestRecovery )
166     {
167         this.requestRecovery = requestRecovery;
168         return this;
169     }
170 
171     /**
172      * Whether or not the {@code Job} should remain stored after it is
173      * orphaned (no {@code Trigger}s point to it).
174      *
175      * @param storeDurably The activation flag
176      * @return This builder instance
177      */
178     public JobSchedulerBuilder withStoreDurably( boolean storeDurably )
179     {
180         this.storeDurably = storeDurably;
181         return this;
182     }
183 
184     /**
185      * Sets the {@code Trigger} name, must be unique within the group.
186      *
187      * @param triggerName The {@code Trigger} name, must be unique within the group
188      * @return This builder instance
189      */
190     public JobSchedulerBuilder withTriggerName( String triggerName )
191     {
192         this.triggerName = triggerName;
193         return this;
194     }
195 
196     /**
197      * Sets the {@code Trigger} group.
198      *
199      * @param triggerGroup The {@code Trigger} group
200      * @return This builder instance
201      */
202     public JobSchedulerBuilder withTriggerGroup( String triggerGroup )
203     {
204         this.triggerGroup = triggerGroup;
205         return this;
206     }
207 
208     /**
209      * Sets the cron expression to base the schedule on.
210      *
211      * @param cronExpression The cron expression to base the schedule on
212      * @return This builder instance
213      */
214     public JobSchedulerBuilder withCronExpression( String cronExpression )
215     {
216         this.cronExpression = cronExpression;
217         return this;
218     }
219 
220     /**
221      * Sets the time zone for which the {@code cronExpression} of this
222      * {@code CronTrigger} will be resolved.
223      *
224      * @param timeZone The time zone for which the {@code cronExpression}
225      *        of this {@code CronTrigger} will be resolved.
226      * @return This builder instance
227      */
228     public JobSchedulerBuilder withTimeZone( TimeZone timeZone )
229     {
230         this.timeZone = timeZone;
231         return this;
232     }
233 
234     /**
235      * Sets the {@code Trigger}'s priority.  When more than one {@code Trigger} have the same
236      * fire time, the scheduler will fire the one with the highest priority
237      * first.
238      *
239      * @param priority The {@code Trigger}'s priority
240      * @return This builder instance
241      */
242     public JobSchedulerBuilder withPriority( int priority )
243     {
244         this.priority = priority;
245         return this;
246     }
247 
248     /**
249      * Sets the {@code Trigger} that will be used to schedule
250      * the {@code Job}.
251      *
252      * <p>
253      * Be aware that using using this method will override any other
254      * {@code Trigger}-related operation, like {@link #withTriggerGroup(String)}
255      * or {@link #withTimeZone(TimeZone)}
256      *
257      * @param trigger The {@code Trigger} to associate with the {@code Job}
258      * @return This builder instance
259      * @since 1.2
260      */
261     public JobSchedulerBuilder withTrigger(Trigger trigger)
262     {
263         this.trigger = trigger;
264         return this;
265     }
266 
267     /**
268      * Requests an existing trigger (sharing the same key as the new trigger) for this job to
269      * be replaced with the new trigger.
270      *
271      * @return This builder instance
272      * @since 1.3
273      */
274     public JobSchedulerBuilder updateExistingTrigger()
275     {
276         this.updateExistingTrigger = true;
277         return this;
278     }
279 
280     /**
281      * Add the produced {@code Job} to the given {@code Scheduler},
282      * and associate the related {@code Trigger} with it.
283      *
284      * Users <b>MUST NOT</b> use this method!
285      *
286      * @param scheduler The given {@code Scheduler}
287      * @throws Exception If any error occurs
288      */
289     @Inject
290     public void schedule( Scheduler scheduler )
291         throws Exception
292     {
293         if ( cronExpression == null && trigger == null )
294         {
295             throw new ProvisionException( format( "Impossible to schedule Job '%s' without cron expression",
296                                                   jobClass.getName() ) );
297         }
298         if ( cronExpression != null && trigger != null )
299         {
300           throw new ProvisionException( format( "Impossible to schedule Job '%s' with cron expression " +
301                                                 "and an associated Trigger at the same time", jobClass.getName() ) );
302         }
303 
304         JobKey jobKey = jobKey( DEFAULT.equals( jobName ) ? jobClass.getName() : jobName, jobGroup );
305         TriggerKey triggerKey = triggerKey( DEFAULT.equals( triggerName ) ? jobClass.getCanonicalName() : triggerName, triggerGroup );
306 
307         if ( updateExistingTrigger && scheduler.checkExists( triggerKey ) ) {
308             scheduler.unscheduleJob( triggerKey );
309         }
310 
311         scheduler.scheduleJob( newJob( jobClass )
312                                .withIdentity( jobKey )
313                                .requestRecovery( requestRecovery )
314                                .storeDurably( storeDurably ).build(),
315                                ( trigger == null ) ?
316                                newTrigger()
317                                .withIdentity( triggerKey )
318                                .withSchedule( cronSchedule( cronExpression )
319                                               .inTimeZone( timeZone ) )
320                                               .withPriority( priority )
321                                               .build()
322                                 : trigger);
323     }
324 
325 }