001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.cron;
018:
019: import java.text.ParseException;
020: import java.util.Arrays;
021: import java.util.Date;
022: import java.util.Map;
023: import java.util.NoSuchElementException;
024:
025: import org.apache.avalon.framework.CascadingException;
026: import org.apache.avalon.framework.activity.Disposable;
027: import org.apache.avalon.framework.activity.Initializable;
028: import org.apache.avalon.framework.activity.Startable;
029: import org.apache.avalon.framework.component.Component;
030: import org.apache.avalon.framework.configuration.Configurable;
031: import org.apache.avalon.framework.configuration.Configuration;
032: import org.apache.avalon.framework.configuration.ConfigurationException;
033: import org.apache.avalon.framework.context.Context;
034: import org.apache.avalon.framework.context.ContextException;
035: import org.apache.avalon.framework.context.Contextualizable;
036: import org.apache.avalon.framework.logger.AbstractLogEnabled;
037: import org.apache.avalon.framework.parameters.Parameters;
038: import org.apache.avalon.framework.service.ServiceException;
039: import org.apache.avalon.framework.service.ServiceManager;
040: import org.apache.avalon.framework.service.Serviceable;
041: import org.apache.avalon.framework.thread.ThreadSafe;
042: import org.apache.cocoon.components.thread.RunnableManager;
043: import org.apache.cocoon.components.thread.ThreadPool;
044: import org.quartz.CronTrigger;
045: import org.quartz.Job;
046: import org.quartz.JobDataMap;
047: import org.quartz.JobDetail;
048: import org.quartz.Scheduler;
049: import org.quartz.SchedulerException;
050: import org.quartz.SimpleTrigger;
051: import org.quartz.Trigger;
052: import org.quartz.JobExecutionContext;
053: import org.quartz.JobExecutionException;
054: import org.quartz.impl.DirectSchedulerFactory;
055: import org.quartz.impl.jdbcjobstore.InvalidConfigurationException;
056: import org.quartz.impl.jdbcjobstore.JobStoreSupport;
057: import org.quartz.simpl.RAMJobStore;
058: import org.quartz.spi.JobStore;
059: import org.quartz.spi.TriggerFiredBundle;
060: import org.quartz.utils.ConnectionProvider;
061: import org.quartz.utils.DBConnectionManager;
062: import org.quartz.utils.JNDIConnectionProvider;
063:
064: /**
065: * This component can either schedule jobs or directly execute one.
066: *
067: * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
068: * @version CVS $Id: QuartzJobScheduler.java 433543 2006-08-22 06:22:54Z crossley $
069: *
070: * @since 2.1.1
071: */
072: public class QuartzJobScheduler extends AbstractLogEnabled implements
073: JobScheduler, Component, ThreadSafe, Serviceable, Configurable,
074: Startable, Disposable, Contextualizable, Initializable {
075:
076: /** Map key for the component role */
077: static final String DATA_MAP_ROLE = "QuartzJobScheduler.ROLE";
078:
079: /** Map key for the job object */
080: static final String DATA_MAP_OBJECT = "QuartzJobScheduler.Object";
081:
082: /** Map key for the job name */
083: static final String DATA_MAP_NAME = "QuartzJobScheduler.JobName";
084:
085: /** Map key for the avalon context */
086: static final String DATA_MAP_CONTEXT = "QuartzJobScheduler.Context";
087:
088: /** Map key for the service manager */
089: static final String DATA_MAP_MANAGER = "QuartzJobScheduler.ServiceManager";
090:
091: /** Map key for the logger */
092: static final String DATA_MAP_LOGGER = "QuartzJobScheduler.Logger";
093:
094: /** Map key for the concurrent run property */
095: static final String DATA_MAP_RUN_CONCURRENT = "QuartzJobScheduler.RunConcurrently";
096:
097: /** Map key for additional Parameters */
098: static final String DATA_MAP_PARAMETERS = "QuartzJobScheduler.Parameters";
099:
100: /** Map key for additional Object Map */
101: static final String DATA_MAP_OBJECTMAP = "QuartzJobScheduler.Map";
102:
103: /* Map key for the last JobExecutionContext
104: static final String DATA_MAP_JOB_EXECUTION_CONTEXT = "QuartzJobScheduler.JobExecutionContext"; */
105:
106: /** Map key for the run status */
107: static final String DATA_MAP_KEY_ISRUNNING = "QuartzJobExecutor.isRunning";
108:
109: /** The group name */
110: static final String DEFAULT_QUARTZ_JOB_GROUP = "Cocoon";
111:
112: /** The scheduler name */
113: static final String DEFAULT_QUARTZ_SCHEDULER_NAME = "Cocoon";
114:
115: /** The Avalon Context instance */
116: private Context context;
117:
118: /** The PooledExecutor instance */
119: private ThreadPool executor;
120:
121: /** The quartz scheduler */
122: private Scheduler scheduler;
123:
124: /** The ServiceManager instance */
125: private ServiceManager manager;
126:
127: /** The configuration, parsed in initialize() */
128: private Configuration config;
129:
130: /** Should we wait for running jobs to terminate on shutdown ? */
131: private boolean m_shutdownGraceful;
132:
133: /* (non-Javadoc)
134: * @see org.apache.cocoon.components.cron.JobScheduler#getJobNames()
135: */
136: public String[] getJobNames() {
137: try {
138: final String[] names = scheduler
139: .getJobNames(DEFAULT_QUARTZ_JOB_GROUP);
140: Arrays.sort(names);
141:
142: return names;
143: } catch (final SchedulerException se) {
144: getLogger().error("could not gather job names", se);
145: }
146:
147: return new String[0];
148: }
149:
150: /* (non-Javadoc)
151: * @see org.apache.cocoon.components.cron.JobScheduler#getSchedulerEntry(java.lang.String)
152: */
153: public JobSchedulerEntry getJobSchedulerEntry(String jobname) {
154: try {
155: return new QuartzJobSchedulerEntry(jobname, scheduler);
156: } catch (final Exception e) {
157: getLogger().error("cannot create QuartzJobSchedulerEntry",
158: e);
159: }
160:
161: return null;
162: }
163:
164: /* (non-Javadoc)
165: * @see org.apache.cocoon.components.cron.JobScheduler#addJob(java.lang.String, java.lang.Object, java.lang.String, boolean, org.apache.avalon.framework.parameters.Parameters, java.util.Map)
166: */
167: public void addJob(final String name, final Object job,
168: final String cronSpec, final boolean canRunConcurrently,
169: final Parameters params, final Map objects)
170: throws CascadingException {
171: final JobDataMap jobDataMap = new JobDataMap();
172: jobDataMap.put(DATA_MAP_OBJECT, job);
173: addJob(name, jobDataMap, cronSpec, canRunConcurrently, params,
174: objects);
175: }
176:
177: /* (non-Javadoc)
178: * @see org.apache.cocoon.components.cron.JobScheduler#addJob(java.lang.String, java.lang.String, java.lang.String, boolean, org.apache.avalon.framework.parameters.Parameters, java.util.Map)
179: */
180: public void addJob(final String name, final String jobrole,
181: final String cronSpec, final boolean canRunConcurrently,
182: final Parameters params, final Map objects)
183: throws CascadingException {
184: final JobDataMap jobDataMap = new JobDataMap();
185: jobDataMap.put(DATA_MAP_ROLE, jobrole);
186: addJob(name, jobDataMap, cronSpec, canRunConcurrently, params,
187: objects);
188: }
189:
190: /* (non-Javadoc)
191: * @see org.apache.cocoon.components.cron.JobScheduler#addJob(java.lang.String, java.lang.Object, java.lang.String, boolean)
192: */
193: public void addJob(final String name, final Object job,
194: final String cronSpec, final boolean canRunConcurrently)
195: throws CascadingException {
196: if (!(job instanceof CronJob) && !(job instanceof Runnable)
197: && !(job instanceof Job)) {
198: throw new CascadingException(
199: "Job object is neither an instance of "
200: + CronJob.class.getName() + ","
201: + Runnable.class.getName() + " nor "
202: + Job.class.getName());
203: }
204:
205: addJob(name, job, cronSpec, canRunConcurrently, null, null);
206: }
207:
208: /* (non-Javadoc)
209: * @see org.apache.cocoon.components.cron.JobScheduler#addJob(java.lang.String, java.lang.String, java.lang.String, boolean)
210: */
211: public void addJob(final String name, final String jobrole,
212: final String cronSpec, final boolean canRunConcurrently)
213: throws CascadingException {
214: addJob(name, jobrole, cronSpec, canRunConcurrently, null, null);
215: }
216:
217: /**
218: * Schedule a period job. Note that if a Job already has same name then it is overwritten.
219: *
220: * @param name the name of the job
221: * @param jobrole The Avalon components role name of the job itself
222: * @param period Every period seconds this job is started
223: * @param canRunConcurrently whether this job can run even previous scheduled runs are still running
224: * @param params additional Parameters to be passed to the job
225: * @param objects additional objects to be passed to the job
226: *
227: * @throws CascadingException in case of failures
228: */
229: public void addPeriodicJob(String name, String jobrole,
230: long period, boolean canRunConcurrently, Parameters params,
231: Map objects) throws CascadingException {
232: final JobDataMap jobDataMap = new JobDataMap();
233: jobDataMap.put(DATA_MAP_ROLE, jobrole);
234:
235: final long ms = period * 1000;
236: final SimpleTrigger timeEntry = new SimpleTrigger(name,
237: DEFAULT_QUARTZ_JOB_GROUP, new Date(System
238: .currentTimeMillis()
239: + ms), null, SimpleTrigger.REPEAT_INDEFINITELY,
240: ms);
241:
242: addJob(name, jobDataMap, timeEntry, canRunConcurrently, params,
243: objects);
244: }
245:
246: /**
247: * Schedule a periodic job. The job is started the first time when the period has passed. Note that if a job with
248: * the same name has already beed added it is overwritten.
249: *
250: * @param name the name of the job
251: * @param job The job object itself. It must implement either CronJob, Runnable or might also be an implementation
252: * specific class (i.e. org.quartz.Job)
253: * @param period Every period seconds this job is started
254: * @param canRunConcurrently whether this job can run even previous scheduled runs are still running
255: * @param params Additional Parameters to setup CronJob
256: * @param objects A Map with additional object to setup CronJob
257: */
258: public void addPeriodicJob(String name, Object job, long period,
259: boolean canRunConcurrently, Parameters params, Map objects)
260: throws CascadingException {
261: if (!(job instanceof CronJob) && !(job instanceof Runnable)
262: && !(job instanceof Job)) {
263: throw new CascadingException(
264: "Job object is neither an instance of "
265: + CronJob.class.getName() + ","
266: + Runnable.class.getName() + " nor "
267: + Job.class.getName());
268: }
269: final JobDataMap jobDataMap = new JobDataMap();
270: jobDataMap.put(DATA_MAP_OBJECT, job);
271:
272: final long ms = period * 1000;
273: final SimpleTrigger timeEntry = new SimpleTrigger(name,
274: DEFAULT_QUARTZ_JOB_GROUP, new Date(System
275: .currentTimeMillis()
276: + ms), null, SimpleTrigger.REPEAT_INDEFINITELY,
277: ms);
278:
279: addJob(name, jobDataMap, timeEntry, canRunConcurrently, params,
280: objects);
281: }
282:
283: /* (non-Javadoc)
284: * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
285: */
286: public void configure(final Configuration config)
287: throws ConfigurationException {
288: this .config = config;
289: }
290:
291: /* (non-Javadoc)
292: * @see org.apache.avalon.framework.activity.Disposable#dispose()
293: */
294: public void dispose() {
295: try {
296: if (getLogger().isInfoEnabled()) {
297: getLogger()
298: .info(
299: "shutting down scheduler "
300: + (m_shutdownGraceful ? "graceful (waiting for running jobs to complete)"
301: : "immediately (killing running jobs)"));
302: }
303:
304: scheduler.shutdown(m_shutdownGraceful);
305: scheduler = null;
306: } catch (final SchedulerException se) {
307: getLogger().error("failure during scheduler shutdown", se);
308: }
309:
310: this .executor = null;
311: }
312:
313: /* (non-Javadoc)
314: * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
315: */
316: public void contextualize(Context context) throws ContextException {
317: this .context = context;
318: }
319:
320: public void initialize() throws Exception {
321: try {
322: // If cocoon reloads (or is it the container that reload us?)
323: // we cannot create the same scheduler again
324: final String runID = new Date().toString()
325: .replace(' ', '_');
326: final QuartzThreadPool pool = createThreadPool(this .config
327: .getChild("thread-pool"));
328: final JobStore store = createJobStore(
329: DEFAULT_QUARTZ_SCHEDULER_NAME, runID, this .config
330: .getChild("store"));
331: DirectSchedulerFactory.getInstance().createScheduler(
332: DEFAULT_QUARTZ_SCHEDULER_NAME, runID, pool, store);
333: // scheduler = DirectSchedulerFactory.getInstance().getScheduler(DEFAULT_QUARTZ_SCHEDULER_NAME, runID);
334: scheduler = DirectSchedulerFactory.getInstance()
335: .getScheduler(DEFAULT_QUARTZ_SCHEDULER_NAME);
336: } catch (final SchedulerException se) {
337: throw new ConfigurationException(
338: "cannot create a quartz scheduler", se);
339: }
340:
341: final Configuration[] triggers = this .config.getChild(
342: "triggers").getChildren("trigger");
343: createTriggers(triggers);
344:
345: // We're finished with the configuration
346: this .config = null;
347:
348: if (getLogger().isDebugEnabled() && (triggers.length == 0)) {
349: getLogger().debug("no triggers configured at startup");
350: }
351: }
352:
353: /* (non-Javadoc)
354: * @see org.apache.cocoon.components.cron.JobScheduler#fireTarget(java.lang.Object)
355: */
356: public boolean fireJob(final Object job) {
357: return fireJob(job.getClass().getName(), job);
358: }
359:
360: /* (non-Javadoc)
361: * @see org.apache.cocoon.components.cron.JobScheduler#fireTarget(java.lang.String)
362: */
363: public boolean fireJob(final String jobrole) {
364: Object job = null;
365:
366: try {
367: job = manager.lookup(jobrole);
368:
369: return fireJob(jobrole, job);
370: } catch (final ServiceException se) {
371: getLogger().error("cannot fire job " + jobrole, se);
372: } finally {
373: manager.release(job);
374: }
375:
376: return false;
377: }
378:
379: /* (non-Javadoc)
380: * @see org.apache.cocoon.components.cron.JobScheduler#fireJob(java.lang.Object, org.apache.avalon.framework.parameters.Parameters, java.util.Map)
381: */
382: public boolean fireJob(final Object job, final Parameters params,
383: final Map objects) throws CascadingException {
384: if (job instanceof ConfigurableCronJob) {
385: ((ConfigurableCronJob) job).setup(params, objects);
386: }
387:
388: return fireJob(job);
389: }
390:
391: /* (non-Javadoc)
392: * @see org.apache.cocoon.components.cron.JobScheduler#fireJob(java.lang.String, org.apache.avalon.framework.parameters.Parameters, java.util.Map)
393: */
394: public boolean fireJob(final String jobrole,
395: final Parameters params, final Map objects)
396: throws CascadingException {
397: Object job = null;
398:
399: try {
400: job = manager.lookup(jobrole);
401:
402: if (job instanceof ConfigurableCronJob) {
403: ((ConfigurableCronJob) job).setup(params, objects);
404: }
405:
406: return fireJob(jobrole, job);
407: } catch (final ServiceException se) {
408: getLogger().error("cannot fire job " + jobrole, se);
409: } finally {
410: manager.release(job);
411: }
412:
413: return false;
414: }
415:
416: /* (non-Javadoc)
417: * @see org.apache.cocoon.components.cron.JobScheduler#fireJobAt(java.util.Date, java.lang.String, java.lang.Object)
418: */
419: public void fireJobAt(final Date date, final String name,
420: final Object job) throws CascadingException {
421: fireJobAt(date, name, job, null, null);
422: }
423:
424: /* (non-Javadoc)
425: * @see org.apache.cocoon.components.cron.JobScheduler#fireJobAt(java.util.Date, java.lang.String, java.lang.String)
426: */
427: public void fireJobAt(final Date date, final String name,
428: final String jobrole) throws CascadingException {
429: fireJobAt(date, name, jobrole, null, null);
430: }
431:
432: /* (non-Javadoc)
433: * @see org.apache.cocoon.components.cron.JobScheduler#fireJobAt(java.util.Date, java.lang.String, java.lang.Object, org.apache.avalon.framework.parameters.Parameters, java.util.Map)
434: */
435: public void fireJobAt(final Date date, final String name,
436: final Object job, final Parameters params, final Map objects)
437: throws CascadingException {
438: final JobDataMap jobDataMap = new JobDataMap();
439: jobDataMap.put(DATA_MAP_OBJECT, job);
440: addJob(name, jobDataMap, date, true, params, objects);
441: }
442:
443: /* (non-Javadoc)
444: * @see org.apache.cocoon.components.cron.JobScheduler#fireJobAt(java.util.Date, java.lang.String, java.lang.String, org.apache.avalon.framework.parameters.Parameters, java.util.Map)
445: */
446: public void fireJobAt(final Date date, final String name,
447: final String jobrole, final Parameters params,
448: final Map objects) throws CascadingException {
449: final JobDataMap jobDataMap = new JobDataMap();
450: jobDataMap.put(DATA_MAP_ROLE, jobrole);
451: addJob(name, jobDataMap, date, true, params, objects);
452: }
453:
454: /* (non-Javadoc)
455: * @see org.apache.cocoon.components.cron.JobScheduler#removeJob(java.lang.String)
456: */
457: public void removeJob(final String name)
458: throws NoSuchElementException {
459: try {
460: if (scheduler.deleteJob(name, DEFAULT_QUARTZ_JOB_GROUP)) {
461: getLogger().info("job " + name + " removed by request");
462: } else {
463: getLogger().error(
464: "couldn't remove requested job " + name);
465: }
466: } catch (final SchedulerException se) {
467: getLogger().error("cannot remove job " + name, se);
468: throw new NoSuchElementException(se.getMessage());
469: }
470: }
471:
472: /* (non-Javadoc)
473: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
474: */
475: public void service(final ServiceManager manager)
476: throws ServiceException {
477: this .manager = manager;
478: }
479:
480: /* (non-Javadoc)
481: * @see org.apache.avalon.framework.activity.Startable#start()
482: */
483: public void start() throws Exception {
484: scheduler.start();
485: }
486:
487: /* (non-Javadoc)
488: * @see org.apache.avalon.framework.activity.Startable#stop()
489: */
490: public void stop() throws Exception {
491: scheduler.standby();
492: }
493:
494: /**
495: * Add a job to the scheduler
496: *
497: * @param name The name of the job to add
498: * @param jobDataMap The JobDataMap to use for this job
499: * @param date the date to schedule this job
500: * @param canRunConcurrently whether this job can be run concurrently
501: * @param params Additional Parameters to setup CronJob
502: * @param objects A Map with additional object to setup CronJob
503: *
504: * @throws CascadingException thrown in case of errors
505: */
506: private void addJob(final String name, final JobDataMap jobDataMap,
507: final Date date, final boolean canRunConcurrently,
508: final Parameters params, final Map objects)
509: throws CascadingException {
510: final SimpleTrigger trigger = new SimpleTrigger(name,
511: DEFAULT_QUARTZ_JOB_GROUP, date);
512: addJob(name, jobDataMap, trigger, canRunConcurrently, params,
513: objects);
514: }
515:
516: /**
517: * Add a job to the scheduler
518: *
519: * @param name The name of the job to add
520: * @param jobDataMap The JobDataMap to use for this job
521: * @param cronSpec a Cron time expression
522: * @param canRunConcurrently whether this job can be run concurrently
523: * @param params Additional Parameters to setup CronJob
524: * @param objects A Map with additional object to setup CronJob
525: *
526: * @throws CascadingException thrown in case of errors
527: */
528: private void addJob(final String name, final JobDataMap jobDataMap,
529: final String cronSpec, final boolean canRunConcurrently,
530: final Parameters params, final Map objects)
531: throws CascadingException {
532: final CronTrigger cronJobEntry = new CronTrigger(name,
533: DEFAULT_QUARTZ_JOB_GROUP);
534:
535: try {
536: cronJobEntry.setCronExpression(cronSpec);
537: } catch (final ParseException pe) {
538: throw new CascadingException(pe.getMessage(), pe);
539: }
540:
541: addJob(name, jobDataMap, cronJobEntry, canRunConcurrently,
542: params, objects);
543: }
544:
545: /**
546: * Add a job to the scheduler
547: *
548: * @param name The name of the job to add
549: * @param jobDataMap The JobDataMap to use for this job
550: * @param trigger a Trigger
551: * @param canRunConcurrently whether this job can be run concurrently
552: * @param params Additional Parameters to setup CronJob (might be null)
553: * @param objects A Map with additional object to setup CronJob (might be null)
554: *
555: * @throws CascadingException thrown in case of errors
556: */
557: private void addJob(final String name, final JobDataMap jobDataMap,
558: final Trigger trigger, final boolean canRunConcurrently,
559: final Parameters params, final Map objects)
560: throws CascadingException {
561: try {
562: final JobDetail jobdetail = scheduler.getJobDetail(name,
563: DEFAULT_QUARTZ_JOB_GROUP);
564: if (jobdetail != null) {
565: removeJob(name);
566: }
567: } catch (final SchedulerException ignored) {
568: }
569:
570: initDataMap(jobDataMap, name, canRunConcurrently, params,
571: objects);
572:
573: final JobDetail detail = createJobDetail(name, jobDataMap);
574:
575: if (getLogger().isInfoEnabled()) {
576: getLogger().info(
577: "Adding CronJob '" + trigger.getFullName() + "'");
578: }
579:
580: try {
581: scheduler.scheduleJob(detail, trigger);
582: } catch (final SchedulerException se) {
583: throw new CascadingException(se.getMessage(), se);
584: }
585:
586: if (getLogger().isDebugEnabled()) {
587: if (trigger instanceof CronTrigger) {
588: getLogger().debug(
589: "Time schedule summary:\n"
590: + ((CronTrigger) trigger)
591: .getExpressionSummary());
592: } else {
593: getLogger().debug(
594: "Next scheduled time: "
595: + trigger.getNextFireTime());
596: }
597: }
598: }
599:
600: protected JobDataMap initDataMap(JobDataMap jobDataMap,
601: String jobName, boolean concurent, Parameters params,
602: Map objects) {
603: jobDataMap.put(DATA_MAP_NAME, jobName);
604: jobDataMap.put(DATA_MAP_LOGGER, getLogger());
605: jobDataMap.put(DATA_MAP_CONTEXT, this .context);
606: jobDataMap.put(DATA_MAP_MANAGER, this .manager);
607: jobDataMap.put(DATA_MAP_RUN_CONCURRENT,
608: concurent ? Boolean.TRUE : Boolean.FALSE);
609: if (null != params) {
610: jobDataMap.put(DATA_MAP_PARAMETERS, params);
611: }
612: if (null != objects) {
613: jobDataMap.put(DATA_MAP_OBJECTMAP, objects);
614: }
615: return jobDataMap;
616: }
617:
618: protected JobDetail createJobDetail(String name,
619: JobDataMap jobDataMap) {
620: final JobDetail detail = new JobDetail(name,
621: DEFAULT_QUARTZ_JOB_GROUP, QuartzJobExecutor.class);
622: detail.setJobDataMap(jobDataMap);
623: return detail;
624: }
625:
626: /**
627: * Create a QuartzThreadPool
628: *
629: * @param poolConfig Configuration element for the thread pool
630: *
631: * @return QuartzThreadPool
632: */
633: private QuartzThreadPool createThreadPool(
634: final Configuration poolConfig) throws ServiceException {
635: final int queueSize = poolConfig.getChild("queue-size")
636: .getValueAsInteger(-1);
637: final int maxPoolSize = poolConfig.getChild("max-pool-size")
638: .getValueAsInteger(-1);
639: final int minPoolSize = poolConfig.getChild("min-pool-size")
640: .getValueAsInteger(-1);
641: final int keepAliveTimeMs = poolConfig.getChild(
642: "keep-alive-time-ms").getValueAsInteger(-1);
643: final String blockPolicy = poolConfig.getChild("block-policy")
644: .getValue(null);
645: m_shutdownGraceful = poolConfig.getChild("shutdown-graceful")
646: .getValueAsBoolean(true);
647:
648: final int shutdownWaitTimeMs = poolConfig.getChild(
649: "shutdown-wait-time-ms").getValueAsInteger(-1);
650: final RunnableManager runnableManager = (RunnableManager) this .manager
651: .lookup(RunnableManager.ROLE);
652: this .executor = runnableManager.createPool(queueSize,
653: maxPoolSize, minPoolSize, Thread.NORM_PRIORITY,
654: false, // no daemon
655: keepAliveTimeMs, blockPolicy, m_shutdownGraceful,
656: shutdownWaitTimeMs);
657: final QuartzThreadPool pool = new QuartzThreadPool(
658: this .executor);
659: return pool;
660: }
661:
662: /**
663: * Create the triggers
664: *
665: * @param triggers array of trigger configuration elements
666: *
667: * @throws ConfigurationException thrown in case of configuration failures
668: */
669: private void createTriggers(final Configuration[] triggers)
670: throws ConfigurationException {
671: for (int i = 0; i < triggers.length; i++) {
672: String cron = triggers[i].getChild("cron").getValue(null);
673:
674: if (null == cron) {
675: final String seconds = triggers[i].getChild("seconds")
676: .getValue("0");
677: final String minutes = triggers[i].getChild("minutes")
678: .getValue("*");
679: final String hours = triggers[i].getChild("hours")
680: .getValue("*");
681: final String days = triggers[i].getChild("days")
682: .getValue("*");
683: final String months = triggers[i].getChild("months")
684: .getValue("*");
685: final String weekdays = triggers[i]
686: .getChild("weekdays").getValue("?");
687: final String years = triggers[i].getChild("years")
688: .getValue("*");
689: cron = seconds + " " + minutes + " " + hours + " "
690: + days + " " + months + " " + weekdays + " "
691: + years;
692: }
693:
694: try {
695: addJob(triggers[i].getAttribute("name"), triggers[i]
696: .getAttribute("target"), cron, triggers[i]
697: .getAttributeAsBoolean("concurrent-runs", true));
698: } catch (final CascadingException ce) {
699: throw new ConfigurationException(
700: "failed adding trigger to scheduler", ce);
701: }
702: }
703: }
704:
705: private JobStore createJobStore(String instanceName,
706: String instanceID, final Configuration configuration)
707: throws ConfigurationException {
708: String type = configuration.getAttribute("type", "ram");
709: if (type.equals("ram")) {
710: return new RAMJobStore();
711: }
712:
713: JobStoreSupport store = null;
714: if (type.equals("tx")) {
715: store = new QuartzJobStoreTX(getLogger(), this .manager,
716: this .context);
717: } else if (type.equals("cmt")) {
718: store = new QuartzJobStoreCMT(getLogger(), this .manager,
719: this .context);
720: } else {
721: throw new ConfigurationException("Unknown store type: "
722: + type);
723: }
724:
725: Configuration dsConfig = configuration.getChild("datasource",
726: false);
727: if (dsConfig == null) {
728: throw new ConfigurationException("Store " + type
729: + " requires datasource configuration.");
730: }
731:
732: String dsName = dsConfig.getValue();
733: String dsType = dsConfig.getAttribute("provider", "jndi");
734:
735: ConnectionProvider provider;
736: if (dsType.equals("jndi")) {
737: provider = new JNDIConnectionProvider(dsName, false);
738: } else if (dsType.equals("excalibur")) {
739: provider = new DataSourceComponentConnectionProvider(
740: dsName, this .manager);
741: } else {
742: // assume class name
743: try {
744: provider = (ConnectionProvider) Class.forName(dsType)
745: .newInstance();
746: } catch (Exception e) {
747: throw new ConfigurationException(
748: "Could not instantiate ConnectionProvider class "
749: + dsType);
750: }
751: }
752:
753: store.setInstanceName(instanceName);
754: store.setInstanceId(instanceID);
755: store.setDataSource(dsType + ":" + dsName);
756: DBConnectionManager.getInstance().addConnectionProvider(
757: dsType + ":" + dsName, provider);
758:
759: String delegate = configuration.getAttribute("delegate", null);
760: try {
761: if (delegate != null) {
762: store.setDriverDelegateClass(delegate);
763: }
764: } catch (InvalidConfigurationException e) {
765: throw new ConfigurationException(
766: "Could not instantiate DriverDelegate class "
767: + delegate, e);
768: }
769:
770: return store;
771: }
772:
773: /* (non-Javadoc)
774: * @see org.apache.cocoon.components.cron.JobScheduler#fireTarget(java.lang.Object)
775: */
776: private boolean fireJob(final String name, final Object job) {
777: try {
778: if (job instanceof CronJob || job instanceof Job) {
779: JobDataMap jobDataMap = new JobDataMap();
780: jobDataMap.put(DATA_MAP_OBJECT, job);
781: initDataMap(jobDataMap, name, true, null, null);
782:
783: final JobDetail detail = createJobDetail(name,
784: jobDataMap);
785:
786: final Trigger trigger = new SimpleTrigger(name,
787: DEFAULT_QUARTZ_JOB_GROUP);
788:
789: TriggerFiredBundle fireBundle = new TriggerFiredBundle(
790: detail, trigger, null, false, null, null, null,
791: null);
792:
793: final Job executor = createJobExecutor();
794: final JobExecutionContext context = new JobExecutionContext(
795: this .scheduler, fireBundle, executor);
796:
797: this .executor.execute(new Runnable() {
798: public void run() {
799: try {
800: executor.execute(context);
801: } catch (JobExecutionException e) {
802: getLogger().error(
803: "Job '" + job + "' died.", e);
804: }
805: }
806: });
807: } else if (job instanceof Runnable) {
808: this .executor.execute((Runnable) job);
809: } else {
810: getLogger().error(
811: "Job named '" + name
812: + "' is of invalid class: "
813: + job.getClass().getName());
814: return false;
815: }
816:
817: return true;
818: } catch (final InterruptedException ie) {
819: getLogger().error("job " + name + " interrupted", ie);
820: }
821:
822: return false;
823: }
824:
825: protected Job createJobExecutor() {
826: return new QuartzJobExecutor();
827: }
828:
829: /**
830: * A QuartzThreadPool for the Quartz Scheduler based on Doug Leas concurrency utilities PooledExecutor
831: *
832: * @author <a href="mailto:giacomo@otego.com">Giacomo Pati</a>
833: * @version CVS $Id: QuartzJobScheduler.java 433543 2006-08-22 06:22:54Z crossley $
834: */
835: private static class QuartzThreadPool extends AbstractLogEnabled
836: implements org.quartz.spi.ThreadPool {
837: /** Our executor thread pool */
838: private ThreadPool executor;
839:
840: /**
841: *
842: */
843: public QuartzThreadPool(final ThreadPool executor) {
844: super ();
845: this .executor = executor;
846: }
847:
848: /* (non-Javadoc)
849: * @see org.quartz.spi.QuartzThreadPool#getPoolSize()
850: */
851: public int getPoolSize() {
852: return this .executor.getMaximumPoolSize();
853: }
854:
855: /* (non-Javadoc)
856: * @see org.quartz.spi.QuartzThreadPool#initialize()
857: */
858: public void initialize() {
859: }
860:
861: /* (non-Javadoc)
862: * @see org.quartz.spi.QuartzThreadPool#runInThread(java.lang.Runnable)
863: */
864: public boolean runInThread(final Runnable job) {
865: try {
866: this .executor.execute(job);
867: } catch (final InterruptedException ie) {
868: getLogger().error("Cronjob failed", ie);
869: }
870:
871: return true;
872: }
873:
874: /* (non-Javadoc)
875: * @see org.quartz.spi.QuartzThreadPool#shutdown(boolean)
876: */
877: public void shutdown(final boolean waitForJobsToComplete) {
878: this.executor.shutdown();
879: }
880: }
881: }
|