View Javadoc

1   package org.apache.turbine.services.schedule;
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 java.text.ParseException;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Set;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.fulcrum.quartz.QuartzScheduler;
30  import org.apache.turbine.services.InitializationException;
31  import org.apache.turbine.services.TurbineBaseService;
32  import org.apache.turbine.services.TurbineServices;
33  import org.apache.turbine.util.TurbineException;
34  import org.quartz.CronScheduleBuilder;
35  import org.quartz.JobBuilder;
36  import org.quartz.JobDetail;
37  import org.quartz.JobKey;
38  import org.quartz.Scheduler;
39  import org.quartz.SchedulerException;
40  import org.quartz.Trigger;
41  import org.quartz.TriggerBuilder;
42  import org.quartz.impl.matchers.GroupMatcher;
43  
44  /**
45   * Service for a quartz scheduler.
46   *
47   * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
48   */
49  public class QuartzSchedulerService
50          extends TurbineBaseService
51          implements ScheduleService
52  {
53      /** Logging */
54      protected static Log log = LogFactory.getLog(ScheduleService.LOGGER_NAME);
55  
56      /** Current status of the scheduler */
57      protected boolean enabled = false;
58  
59      /** The Quartz scheduler instance */
60      private Scheduler scheduler;
61  
62      /**
63       * Initializes the SchedulerService.
64       *
65       * @throws InitializationException Something went wrong in the init
66       *         stage
67       */
68      @Override
69      public void init()
70              throws InitializationException
71      {
72          setEnabled(getConfiguration().getBoolean("enabled", true));
73          QuartzScheduler qs = (QuartzScheduler) TurbineServices.getInstance()
74              .getService(QuartzScheduler.class.getName());
75          this.scheduler = qs.getScheduler();
76  
77          restart();
78          setInit(true);
79      }
80  
81      /**
82       * Shutdowns the service.
83       *
84       * This methods interrupts the housekeeping thread.
85       */
86      @Override
87      public void shutdown()
88      {
89          try
90          {
91              this.scheduler.shutdown();
92          }
93          catch (SchedulerException e)
94          {
95              log.error("Could not shut down the scheduler service", e);
96          }
97      }
98  
99      /**
100      * @see org.apache.turbine.services.schedule.ScheduleService#newJob(int, int, int, int, int, java.lang.String)
101      */
102     @Override
103     public JobEntry newJob(int sec, int min, int hour, int wd, int day_mo, String task) throws TurbineException
104     {
105         try
106         {
107             JobDetail jd = JobBuilder.newJob(JobEntryQuartz.class)
108                     .withIdentity(task, JobEntryQuartz.DEFAULT_JOB_GROUP_NAME)
109                     .build();
110 
111             CronScheduleBuilder csb = createCronExpression(sec, min, hour, wd, day_mo);
112 
113             Trigger t = TriggerBuilder.newTrigger()
114                     .withIdentity(task, JobEntryQuartz.DEFAULT_JOB_GROUP_NAME)
115                     .withSchedule(csb)
116                     .forJob(jd)
117                     .build();
118 
119             JobEntryQuartz jeq = new JobEntryQuartz(t, jd);
120 
121             return jeq;
122         }
123         catch (ParseException e)
124         {
125             throw new TurbineException("Could not create scheduled job " + task, e);
126         }
127     }
128 
129     /**
130      * Create a Cron expression from separate elements
131      *
132      * @param sec Value for entry "seconds".
133      * @param min Value for entry "minutes".
134      * @param hour Value for entry "hours".
135      * @param wd Value for entry "week days".
136      * @param day_mo Value for entry "month days".
137      * @return a CronScheduleBuilder
138      * @throws ParseException if the expression is invalid
139      */
140     private CronScheduleBuilder createCronExpression(int sec, int min, int hour, int wd, int day_mo) throws ParseException
141     {
142         StringBuilder sb = new StringBuilder();
143         sb.append(sec == -1 ? "*" : String.valueOf(sec)).append(' ');
144         sb.append(min == -1 ? "*" : String.valueOf(min)).append(' ');
145         sb.append(hour == -1 ? "*" : String.valueOf(hour)).append(' ');
146         if (day_mo == -1)
147         {
148             sb.append(wd == -1 ? "*" : "?").append(' ');
149         }
150         else
151         {
152             sb.append(day_mo).append(' ');
153         }
154         sb.append("* "); // Month not supported
155         if (day_mo == -1)
156         {
157             sb.append(wd == -1 ? "?" : String.valueOf(wd));
158         }
159         else
160         {
161             sb.append("*");
162         }
163 
164         return CronScheduleBuilder.cronSchedule(sb.toString());
165     }
166 
167     /**
168      * Get a specific Job from Storage.
169      *
170      * @param oid The int id for the job.
171      * @return A JobEntry.
172      * @exception TurbineException job could not be retrieved.
173      */
174     @Override
175     public JobEntry getJob(int oid)
176             throws TurbineException
177     {
178         for (JobEntry je : listJobs())
179         {
180             if (je.getJobId() == oid)
181             {
182                 return je;
183             }
184         }
185 
186         throw new TurbineException("Could not retrieve scheduled job with id " + oid);
187     }
188 
189     /**
190      * Add a new job to the queue.
191      *
192      * @param je A JobEntry with the job to add.
193      * @throws TurbineException job could not be added
194      */
195     @Override
196     public void addJob(JobEntry je)
197             throws TurbineException
198     {
199         try
200         {
201             // Update the scheduler.
202             JobEntryQuartz jq = downCast(je);
203             this.scheduler.scheduleJob(jq.getJobDetail(), jq.getJobTrigger());
204         }
205         catch (SchedulerException e)
206         {
207             throw new TurbineException("Problem adding Scheduled Job: " + je.getTask(), e);
208         }
209     }
210 
211     /**
212      * Remove a job from the queue.
213      *
214      * @param je A JobEntry with the job to remove.
215      * @exception TurbineException job could not be removed
216      */
217     @Override
218     public void removeJob(JobEntry je)
219             throws TurbineException
220     {
221         try
222         {
223             JobEntryQuartz jq = downCast(je);
224             this.scheduler.deleteJob(jq.getJobTrigger().getJobKey());
225 
226         }
227         catch (SchedulerException e)
228         {
229             throw new TurbineException("Problem removing Scheduled Job: " + je.getTask(), e);
230         }
231     }
232 
233     /**
234      * Add or update a job.
235      *
236      * @param je A JobEntry with the job to modify
237      * @throws TurbineException job could not be updated
238      */
239     @Override
240     public void updateJob(JobEntry je)
241             throws TurbineException
242     {
243         try
244         {
245             // Update the scheduler.
246             JobEntryQuartz jq = downCast(je);
247             this.scheduler.rescheduleJob(jq.getJobTrigger().getKey(), jq.getJobTrigger());
248         }
249         catch (SchedulerException e)
250         {
251             throw new TurbineException("Problem updating Scheduled Job: " + je.getTask(), e);
252         }
253     }
254 
255     /**
256      * List jobs in the queue.  This is used by the scheduler UI.
257      *
258      * @return A List of jobs.
259      */
260     @Override
261     public List<? extends JobEntry> listJobs()
262     {
263         List<JobEntryQuartz> jobs = new ArrayList<JobEntryQuartz>();
264 
265         try
266         {
267             @SuppressWarnings("unchecked") // See QTZ-184
268             GroupMatcher<JobKey> groupMatcher = GroupMatcher.groupEquals(JobEntryQuartz.DEFAULT_JOB_GROUP_NAME);
269             Set<JobKey> jobKeys = scheduler.getJobKeys(groupMatcher);
270             for (JobKey jk : jobKeys)
271             {
272                 List<? extends Trigger> triggers = this.scheduler.getTriggersOfJob(jk);
273 
274                 if (triggers == null || triggers.isEmpty())
275                 {
276                     continue; // skip
277                 }
278                 JobDetail jd = this.scheduler.getJobDetail(jk);
279                 JobEntryQuartz job = new JobEntryQuartz(triggers.get(0), jd);
280                 job.setJobId(jk.hashCode());
281                 jobs.add(job);
282             }
283         }
284         catch (SchedulerException e)
285         {
286             log.error("Problem listing Scheduled Jobs", e);
287         }
288 
289         return jobs;
290     }
291 
292 
293     /**
294      * Sets the enabled status of the scheduler
295      *
296      * @param enabled
297      *
298      */
299     protected void setEnabled(boolean enabled)
300     {
301         this.enabled = enabled;
302     }
303 
304     /**
305      * Determines if the scheduler service is currently enabled.
306      *
307      * @return Status of the scheduler service.
308      */
309     @Override
310     public boolean isEnabled()
311     {
312         return enabled;
313     }
314 
315     /**
316      * Starts or restarts the scheduler if not already running.
317      */
318     @Override
319     public synchronized void startScheduler()
320     {
321         setEnabled(true);
322         restart();
323     }
324 
325     /**
326      * Stops the scheduler if it is currently running.
327      */
328     @Override
329     public synchronized void stopScheduler()
330     {
331         log.info("Stopping job scheduler");
332         try
333         {
334             this.scheduler.standby();
335             enabled = false;
336         }
337         catch (SchedulerException e)
338         {
339             log.error("Could not stop scheduler", e);
340         }
341     }
342 
343     /**
344      * Start (or restart) a thread to process commands, or wake up an
345      * existing thread if one is already running.  This method can be
346      * invoked if the background thread crashed due to an
347      * unrecoverable exception in an executed command.
348      */
349     public synchronized void restart()
350     {
351         if (enabled)
352         {
353             log.info("Starting job scheduler");
354             try
355             {
356                 if (!this.scheduler.isStarted())
357                 {
358                     this.scheduler.start();
359                 }
360                 else
361                 {
362                     notify();
363                 }
364             }
365             catch (SchedulerException e)
366             {
367                 log.error("Could not start scheduler", e);
368             }
369         }
370     }
371 
372     /**
373      * @param je a generic job entry
374      * @throws TurbineException
375      *
376      * @return A downcasted JobEntry type
377      */
378     private JobEntryQuartz downCast(JobEntry je) throws TurbineException
379     {
380         if (je instanceof JobEntryQuartz)
381         {
382             return (JobEntryQuartz)je;
383         }
384         else
385         {
386             throw new TurbineException("Invalid job type for this scheduler " + je.getClass());
387         }
388     }
389 }
390