View Javadoc
1   package org.apache.fulcrum.quartz.impl;
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  
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Properties;
26  import java.util.Set;
27  
28  import org.apache.avalon.framework.activity.Disposable;
29  import org.apache.avalon.framework.activity.Initializable;
30  import org.apache.avalon.framework.activity.Startable;
31  import org.apache.avalon.framework.configuration.Configurable;
32  import org.apache.avalon.framework.configuration.Configuration;
33  import org.apache.avalon.framework.configuration.ConfigurationException;
34  import org.apache.avalon.framework.logger.AbstractLogEnabled;
35  import org.apache.avalon.framework.logger.LogEnabled;
36  import org.apache.avalon.framework.parameters.Parameters;
37  import org.apache.avalon.framework.service.ServiceException;
38  import org.apache.avalon.framework.service.ServiceManager;
39  import org.apache.avalon.framework.service.Serviceable;
40  import org.apache.avalon.framework.thread.ThreadSafe;
41  import org.apache.fulcrum.quartz.QuartzScheduler;
42  import org.quartz.Job;
43  import org.quartz.JobDetail;
44  import org.quartz.JobExecutionContext;
45  import org.quartz.JobExecutionException;
46  import org.quartz.JobKey;
47  import org.quartz.JobListener;
48  import org.quartz.Matcher;
49  import org.quartz.Scheduler;
50  import org.quartz.SchedulerException;
51  import org.quartz.Trigger;
52  import org.quartz.impl.StdSchedulerFactory;
53  import org.quartz.impl.matchers.GroupMatcher;
54  
55  /**
56   * Avalon service  wrapping the QuartzScheduler.
57   */
58  public class QuartzSchedulerImpl
59          extends AbstractLogEnabled
60          implements QuartzScheduler, Configurable, Serviceable, Disposable, Initializable, ThreadSafe, JobListener, Startable
61  {
62      /** Configuration key */
63      private static final String CONFIG_CONFIGURATION = "configuration";
64  
65      /** Configuration key */
66      private static final String CONFIG_PROPERTY_FILE = "quartzPropertyFile";
67  
68      /** Configuration key */
69      private static final String CONFIG_PROPERTIES = "properties";
70  
71      /**
72       * the Avalon service serviceManager
73       */
74      private ServiceManager serviceManager;
75  
76      /**
77       * the Quartz scheduler instance
78       */
79      private Scheduler scheduler;
80  
81      /**
82       * the quartz property file
83       */
84      private String quartzPropertyFile;
85  
86      /**
87       * the quartz properties loaded from the XML configuration
88       */
89      private Properties quartzProperties;
90  
91      // === Avalon Lifecycle =================================================
92  
93      /**
94       * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
95       */
96      @Override
97      public void configure(Configuration conf) throws ConfigurationException
98      {
99          Configuration quartzConf = conf.getChild(CONFIG_CONFIGURATION, true);
100 
101         if(quartzConf.getChild(CONFIG_PROPERTIES, false) != null)
102         {
103             this.quartzProperties = Parameters.toProperties(Parameters.fromConfiguration(quartzConf.getChild(CONFIG_PROPERTIES)));
104         }
105         else if(quartzConf.getChild(CONFIG_PROPERTY_FILE, false) != null)
106         {
107             this.quartzPropertyFile = quartzConf.getChild(CONFIG_PROPERTY_FILE).getValue();
108         }
109     }
110 
111     /**
112      * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
113      */
114     @Override
115     public void service(ServiceManager manager) throws ServiceException
116     {
117         this.serviceManager = manager;
118     }
119 
120     /**
121      * @see org.apache.avalon.framework.activity.Initializable#initialize()
122      */
123     @Override
124     public void initialize() throws Exception
125     {
126         // instantiating a specific scheduler from a property file or properties
127         StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
128         if(this.quartzProperties != null)
129         {
130             getLogger().info("Pulling quartz configuration from the container XML configuration");
131             schedulerFactory.initialize(this.quartzProperties);
132         }
133         else if(this.quartzPropertyFile != null)
134         {
135             getLogger().info("Pulling quartz configuration from the following property file : " + this.quartzPropertyFile);
136             schedulerFactory.initialize(this.quartzPropertyFile);
137         }
138         else
139         {
140             getLogger().info("Using Quartz default configuration since no user-supplied configuration was found");
141             schedulerFactory.initialize();
142         }
143 
144         this.scheduler = schedulerFactory.getScheduler();
145 
146         // add this service instance as JobListener to allow basic monitoring
147         getScheduler().getListenerManager().addJobListener(this, new ArrayList<Matcher<JobKey>>());
148     }
149 
150     @Override
151     public void start() throws Exception
152     {
153         getScheduler().start();
154 
155         if(getLogger().isInfoEnabled())
156         {
157             logSchedulerConfiguration();
158         }
159 
160     }
161 
162     @Override
163     public void stop() throws Exception
164     {
165         getScheduler().standby();
166     }
167 
168     /**
169      * @see org.apache.avalon.framework.activity.Disposable#dispose()
170      */
171     @Override
172     public void dispose()
173     {
174         try
175         {
176             // shutdown() does not return until executing Jobs complete execution
177             this.scheduler.shutdown(true);
178         }
179         catch (SchedulerException e)
180         {
181             this.getLogger().warn("Problem shutting down quartz scheduler ", e);
182         }
183 
184         this.scheduler = null;
185         this.serviceManager = null;
186     }
187 
188     // === Service Interface Implementation =================================
189 
190     /**
191      * @see org.apache.fulcrum.quartz.QuartzScheduler#getScheduler()
192      */
193     @Override
194     public Scheduler getScheduler()
195     {
196         return scheduler;
197     }
198 
199     /**
200      * Calls getName() on jobListener
201      *
202      * @see org.quartz.JobListener#getName()
203      */
204     @Override
205     public String getName()
206     {
207         return getClass().getName();
208     }
209 
210     /**
211      * Hook to support jobs implementing Avalon interface such as
212      * LogEnabled and Serviceable.
213      *
214      * @see org.quartz.JobListener#jobToBeExecuted(org.quartz.JobExecutionContext)
215      */
216     @Override
217     public void jobToBeExecuted(JobExecutionContext context)
218     {
219         Job job = context.getJobInstance();
220 
221         // inject a logger instance
222         if(job instanceof LogEnabled)
223         {
224             ((LogEnabled) job).enableLogging(getLogger());
225         }
226 
227         // inject a ServiceManager instance
228         if (job instanceof Serviceable)
229         {
230             try
231             {
232                 ((Serviceable) job).service(serviceManager);
233             }
234             catch (ServiceException e)
235             {
236                 getLogger().error("Error servicing Job[" + job + "]", e);
237             }
238         }
239     }
240 
241     /**
242      * @see org.quartz.JobListener#jobWasExecuted(org.quartz.JobExecutionContext, org.quartz.JobExecutionException)
243      */
244     @Override
245     public void jobWasExecuted(JobExecutionContext context, JobExecutionException ex)
246     {
247         if (ex != null)
248         {
249             String msg = "Executing the job '" + context.getJobDetail().getKey() + "' failed";
250             getLogger().error(msg, ex.getCause());
251         }
252         else
253         {
254             if (getLogger().isDebugEnabled())
255             {
256                 getLogger().debug("Executing the job '" + context.getJobDetail().getKey() + "' took " + context.getJobRunTime() + " ms");
257             }
258         }
259     }
260 
261     /**
262      * @see org.quartz.JobListener#jobExecutionVetoed(org.quartz.JobExecutionContext)
263      */
264     @Override
265     public void jobExecutionVetoed(JobExecutionContext context)
266     {
267         // nothing to do
268     }
269 
270     // === Service Implementation ===========================================
271     /**
272      * @throws SchedulerException generic exception
273      */
274     private void logSchedulerConfiguration() throws SchedulerException
275     {
276         for (String jobGroup : getScheduler().getJobGroupNames())
277         {
278             Set<JobKey> jobsInGroup = getScheduler().getJobKeys(GroupMatcher.jobGroupEquals(jobGroup));
279             getLogger().info("Job Group: " + jobGroup + " contains the following number of jobs : " + jobsInGroup.size());
280             for (JobKey jobKey : jobsInGroup)
281             {
282                 StringBuilder buffer = new StringBuilder();
283                 JobDetail jobDetail = getScheduler().getJobDetail(jobKey);
284                 List<? extends Trigger> jobTriggers = getScheduler().getTriggersOfJob(jobKey);
285                 buffer.append(jobDetail.getKey());
286                 buffer.append(" => ");
287                 if(jobTriggers != null && !jobTriggers.isEmpty())
288                 {
289                     Trigger jt = jobTriggers.get(0);
290                     buffer.append(jt.getKey());
291                     buffer.append(" (");
292                     buffer.append(jt.getNextFireTime());
293                     buffer.append(")");
294                 }
295                 else
296                 {
297                     buffer.append("no trigger defined");
298                 }
299 
300                 getLogger().info(buffer.toString());
301             }
302         }
303     }
304 }