001package org.apache.fulcrum.quartz.impl; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 023import java.util.ArrayList; 024import java.util.List; 025import java.util.Properties; 026import java.util.Set; 027 028import org.apache.avalon.framework.activity.Disposable; 029import org.apache.avalon.framework.activity.Initializable; 030import org.apache.avalon.framework.activity.Startable; 031import org.apache.avalon.framework.configuration.Configurable; 032import org.apache.avalon.framework.configuration.Configuration; 033import org.apache.avalon.framework.configuration.ConfigurationException; 034import org.apache.avalon.framework.logger.AbstractLogEnabled; 035import org.apache.avalon.framework.logger.LogEnabled; 036import org.apache.avalon.framework.parameters.Parameters; 037import org.apache.avalon.framework.service.ServiceException; 038import org.apache.avalon.framework.service.ServiceManager; 039import org.apache.avalon.framework.service.Serviceable; 040import org.apache.avalon.framework.thread.ThreadSafe; 041import org.apache.fulcrum.quartz.QuartzScheduler; 042import org.quartz.Job; 043import org.quartz.JobDetail; 044import org.quartz.JobExecutionContext; 045import org.quartz.JobExecutionException; 046import org.quartz.JobKey; 047import org.quartz.JobListener; 048import org.quartz.Matcher; 049import org.quartz.Scheduler; 050import org.quartz.SchedulerException; 051import org.quartz.Trigger; 052import org.quartz.impl.StdSchedulerFactory; 053import org.quartz.impl.matchers.GroupMatcher; 054 055/** 056 * Avalon service wrapping the QuartzScheduler. 057 */ 058public class QuartzSchedulerImpl 059 extends AbstractLogEnabled 060 implements QuartzScheduler, Configurable, Serviceable, Disposable, Initializable, ThreadSafe, JobListener, Startable 061{ 062 /** Configuration key */ 063 private static final String CONFIG_CONFIGURATION = "configuration"; 064 065 /** Configuration key */ 066 private static final String CONFIG_PROPERTY_FILE = "quartzPropertyFile"; 067 068 /** Configuration key */ 069 private static final String CONFIG_PROPERTIES = "properties"; 070 071 /** 072 * the Avalon service serviceManager 073 */ 074 private ServiceManager serviceManager; 075 076 /** 077 * the Quartz scheduler instance 078 */ 079 private Scheduler scheduler; 080 081 /** 082 * the quartz property file 083 */ 084 private String quartzPropertyFile; 085 086 /** 087 * the quartz properties loaded from the XML configuration 088 */ 089 private Properties quartzProperties; 090 091 // === Avalon Lifecycle ================================================= 092 093 /** 094 * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) 095 */ 096 @Override 097 public void configure(Configuration conf) throws ConfigurationException 098 { 099 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}