| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.components.cron; |
| |
| import java.text.ParseException; |
| import java.util.Arrays; |
| import java.util.Date; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| |
| import org.apache.avalon.framework.CascadingException; |
| import org.apache.avalon.framework.activity.Disposable; |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.activity.Startable; |
| import org.apache.avalon.framework.component.Component; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| import org.apache.avalon.framework.logger.AbstractLogEnabled; |
| import org.apache.avalon.framework.parameters.Parameters; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.avalon.framework.thread.ThreadSafe; |
| import org.apache.cocoon.components.thread.RunnableManager; |
| import org.apache.cocoon.components.thread.ThreadPool; |
| import org.quartz.CronTrigger; |
| import org.quartz.Job; |
| import org.quartz.JobDataMap; |
| import org.quartz.JobDetail; |
| import org.quartz.Scheduler; |
| import org.quartz.SchedulerException; |
| import org.quartz.SimpleTrigger; |
| import org.quartz.Trigger; |
| import org.quartz.JobExecutionContext; |
| import org.quartz.JobExecutionException; |
| import org.quartz.impl.DirectSchedulerFactory; |
| import org.quartz.impl.jdbcjobstore.InvalidConfigurationException; |
| import org.quartz.impl.jdbcjobstore.JobStoreSupport; |
| import org.quartz.simpl.RAMJobStore; |
| import org.quartz.spi.JobStore; |
| import org.quartz.spi.TriggerFiredBundle; |
| import org.quartz.utils.ConnectionProvider; |
| import org.quartz.utils.DBConnectionManager; |
| import org.quartz.utils.JNDIConnectionProvider; |
| |
| /** |
| * This component can either schedule jobs or directly execute one. |
| * |
| * @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a> |
| * @version CVS $Id$ |
| * |
| * @since 2.1.1 |
| */ |
| public class QuartzJobScheduler extends AbstractLogEnabled |
| implements JobScheduler, Component, ThreadSafe, |
| Serviceable, Configurable, Startable, |
| Disposable, Contextualizable, Initializable { |
| |
| /** Map key for the component role */ |
| static final String DATA_MAP_ROLE = "QuartzJobScheduler.ROLE"; |
| |
| /** Map key for the job object */ |
| static final String DATA_MAP_OBJECT = "QuartzJobScheduler.Object"; |
| |
| /** Map key for the job name */ |
| static final String DATA_MAP_NAME = "QuartzJobScheduler.JobName"; |
| |
| /** Map key for the avalon context */ |
| static final String DATA_MAP_CONTEXT = "QuartzJobScheduler.Context"; |
| |
| /** Map key for the service manager */ |
| static final String DATA_MAP_MANAGER = "QuartzJobScheduler.ServiceManager"; |
| |
| /** Map key for the logger */ |
| static final String DATA_MAP_LOGGER = "QuartzJobScheduler.Logger"; |
| |
| /** Map key for the concurrent run property */ |
| static final String DATA_MAP_RUN_CONCURRENT = "QuartzJobScheduler.RunConcurrently"; |
| |
| /** Map key for additional Parameters */ |
| static final String DATA_MAP_PARAMETERS = "QuartzJobScheduler.Parameters"; |
| |
| /** Map key for additional Object Map */ |
| static final String DATA_MAP_OBJECTMAP = "QuartzJobScheduler.Map"; |
| |
| /* Map key for the last JobExecutionContext |
| static final String DATA_MAP_JOB_EXECUTION_CONTEXT = "QuartzJobScheduler.JobExecutionContext"; */ |
| |
| /** Map key for the run status */ |
| static final String DATA_MAP_KEY_ISRUNNING = "QuartzJobExecutor.isRunning"; |
| |
| |
| /** The group name */ |
| static final String DEFAULT_QUARTZ_JOB_GROUP = "Cocoon"; |
| |
| /** The scheduler name */ |
| static final String DEFAULT_QUARTZ_SCHEDULER_NAME = "Cocoon"; |
| |
| /** The Avalon Context instance */ |
| private Context context; |
| |
| /** The PooledExecutor instance */ |
| private ThreadPool executor; |
| |
| /** The quartz scheduler */ |
| private Scheduler scheduler; |
| |
| /** The ServiceManager instance */ |
| private ServiceManager manager; |
| |
| /** The configuration, parsed in initialize() */ |
| private Configuration config; |
| |
| /** Should we wait for running jobs to terminate on shutdown ? */ |
| private boolean m_shutdownGraceful; |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#getJobNames() |
| */ |
| public String[] getJobNames() { |
| try { |
| final String[] names = scheduler.getJobNames(DEFAULT_QUARTZ_JOB_GROUP); |
| Arrays.sort(names); |
| |
| return names; |
| } catch (final SchedulerException se) { |
| getLogger().error("could not gather job names", se); |
| } |
| |
| return new String[0]; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#getSchedulerEntry(java.lang.String) |
| */ |
| public JobSchedulerEntry getJobSchedulerEntry(String jobname) { |
| try { |
| return new QuartzJobSchedulerEntry(jobname, scheduler); |
| } catch (final Exception e) { |
| getLogger().error("cannot create QuartzJobSchedulerEntry", e); |
| } |
| |
| return null; |
| } |
| |
| /* (non-Javadoc) |
| * @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) |
| */ |
| public void addJob(final String name, final Object job, final String cronSpec, final boolean canRunConcurrently, |
| final Parameters params, final Map objects) |
| throws CascadingException { |
| final JobDataMap jobDataMap = new JobDataMap(); |
| jobDataMap.put(DATA_MAP_OBJECT, job); |
| addJob(name, jobDataMap, cronSpec, canRunConcurrently, params, objects); |
| } |
| |
| /* (non-Javadoc) |
| * @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) |
| */ |
| public void addJob(final String name, final String jobrole, final String cronSpec, |
| final boolean canRunConcurrently, final Parameters params, final Map objects) |
| throws CascadingException { |
| final JobDataMap jobDataMap = new JobDataMap(); |
| jobDataMap.put(DATA_MAP_ROLE, jobrole); |
| addJob(name, jobDataMap, cronSpec, canRunConcurrently, params, objects); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#addJob(java.lang.String, java.lang.Object, java.lang.String, boolean) |
| */ |
| public void addJob(final String name, final Object job, final String cronSpec, final boolean canRunConcurrently) |
| throws CascadingException { |
| if (!(job instanceof CronJob) && !(job instanceof Runnable) && !(job instanceof Job)) { |
| throw new CascadingException("Job object is neither an instance of " + CronJob.class.getName() + "," + |
| Runnable.class.getName() + " nor " + Job.class.getName()); |
| } |
| |
| addJob(name, job, cronSpec, canRunConcurrently, null, null); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#addJob(java.lang.String, java.lang.String, java.lang.String, boolean) |
| */ |
| public void addJob(final String name, final String jobrole, final String cronSpec, final boolean canRunConcurrently) |
| throws CascadingException { |
| addJob(name, jobrole, cronSpec, canRunConcurrently, null, null); |
| } |
| |
| /** |
| * Schedule a period job. Note that if a Job already has same name then it is overwritten. |
| * |
| * @param name the name of the job |
| * @param jobrole The Avalon components role name of the job itself |
| * @param period Every period seconds this job is started |
| * @param canRunConcurrently whether this job can run even previous scheduled runs are still running |
| * @param params additional Parameters to be passed to the job |
| * @param objects additional objects to be passed to the job |
| * |
| * @throws CascadingException in case of failures |
| */ |
| public void addPeriodicJob(String name, String jobrole, long period, boolean canRunConcurrently, Parameters params, |
| Map objects) |
| throws CascadingException { |
| final JobDataMap jobDataMap = new JobDataMap(); |
| jobDataMap.put(DATA_MAP_ROLE, jobrole); |
| |
| final long ms = period * 1000; |
| final SimpleTrigger timeEntry = |
| new SimpleTrigger(name, DEFAULT_QUARTZ_JOB_GROUP, new Date(System.currentTimeMillis() + ms), null, |
| SimpleTrigger.REPEAT_INDEFINITELY, ms); |
| |
| addJob(name, jobDataMap, timeEntry, canRunConcurrently, params, objects); |
| } |
| |
| /** |
| * Schedule a periodic job. The job is started the first time when the period has passed. Note that if a job with |
| * the same name has already beed added it is overwritten. |
| * |
| * @param name the name of the job |
| * @param job The job object itself. It must implement either CronJob, Runnable or might also be an implementation |
| * specific class (i.e. org.quartz.Job) |
| * @param period Every period seconds this job is started |
| * @param canRunConcurrently whether this job can run even previous scheduled runs are still running |
| * @param params Additional Parameters to setup CronJob |
| * @param objects A Map with additional object to setup CronJob |
| */ |
| public void addPeriodicJob(String name, Object job, long period, boolean canRunConcurrently, Parameters params, |
| Map objects) |
| throws CascadingException { |
| if (!(job instanceof CronJob) && !(job instanceof Runnable) && !(job instanceof Job)) { |
| throw new CascadingException("Job object is neither an instance of " + CronJob.class.getName() + "," + |
| Runnable.class.getName() + " nor " + Job.class.getName()); |
| } |
| final JobDataMap jobDataMap = new JobDataMap(); |
| jobDataMap.put(DATA_MAP_OBJECT, job); |
| |
| final long ms = period * 1000; |
| final SimpleTrigger timeEntry = |
| new SimpleTrigger(name, DEFAULT_QUARTZ_JOB_GROUP, new Date(System.currentTimeMillis() + ms), null, |
| SimpleTrigger.REPEAT_INDEFINITELY, ms); |
| |
| addJob(name, jobDataMap, timeEntry, canRunConcurrently, params, objects); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) |
| */ |
| public void configure(final Configuration config) |
| throws ConfigurationException { |
| this.config = config; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.activity.Disposable#dispose() |
| */ |
| public void dispose() { |
| try { |
| if (getLogger().isInfoEnabled()) { |
| getLogger().info("shutting down scheduler " + |
| (m_shutdownGraceful ? "graceful (waiting for running jobs to complete)" |
| : "immediately (killing running jobs)")); |
| } |
| |
| scheduler.shutdown(m_shutdownGraceful); |
| scheduler = null; |
| } catch (final SchedulerException se) { |
| getLogger().error("failure during scheduler shutdown", se); |
| } |
| |
| this.executor = null; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) |
| */ |
| public void contextualize(Context context) throws ContextException { |
| this.context = context; |
| } |
| |
| public void initialize() throws Exception { |
| try { |
| // If cocoon reloads (or is it the container that reload us?) |
| // we cannot create the same scheduler again |
| final String runID = new Date().toString().replace(' ', '_'); |
| final QuartzThreadPool pool = createThreadPool(this.config.getChild("thread-pool")); |
| final JobStore store = createJobStore(DEFAULT_QUARTZ_SCHEDULER_NAME, runID, this.config.getChild("store")); |
| DirectSchedulerFactory.getInstance().createScheduler(DEFAULT_QUARTZ_SCHEDULER_NAME, runID, pool, store); |
| // scheduler = DirectSchedulerFactory.getInstance().getScheduler(DEFAULT_QUARTZ_SCHEDULER_NAME, runID); |
| scheduler = DirectSchedulerFactory.getInstance().getScheduler(DEFAULT_QUARTZ_SCHEDULER_NAME); |
| } catch (final SchedulerException se) { |
| throw new ConfigurationException("cannot create a quartz scheduler", se); |
| } |
| |
| final Configuration[] triggers = this.config.getChild("triggers").getChildren("trigger"); |
| createTriggers(triggers); |
| |
| // We're finished with the configuration |
| this.config = null; |
| |
| if (getLogger().isDebugEnabled() && (triggers.length == 0)) { |
| getLogger().debug("no triggers configured at startup"); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#fireTarget(java.lang.Object) |
| */ |
| public boolean fireJob(final Object job) { |
| return fireJob(job.getClass().getName(), job); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#fireTarget(java.lang.String) |
| */ |
| public boolean fireJob(final String jobrole) { |
| Object job = null; |
| |
| try { |
| job = manager.lookup(jobrole); |
| |
| return fireJob(jobrole, job); |
| } catch (final ServiceException se) { |
| getLogger().error("cannot fire job " + jobrole, se); |
| } finally { |
| manager.release(job); |
| } |
| |
| return false; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#fireJob(java.lang.Object, org.apache.avalon.framework.parameters.Parameters, java.util.Map) |
| */ |
| public boolean fireJob(final Object job, final Parameters params, final Map objects) |
| throws CascadingException { |
| if (job instanceof ConfigurableCronJob) { |
| ((ConfigurableCronJob)job).setup(params, objects); |
| } |
| |
| return fireJob(job); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#fireJob(java.lang.String, org.apache.avalon.framework.parameters.Parameters, java.util.Map) |
| */ |
| public boolean fireJob(final String jobrole, final Parameters params, final Map objects) |
| throws CascadingException { |
| Object job = null; |
| |
| try { |
| job = manager.lookup(jobrole); |
| |
| if (job instanceof ConfigurableCronJob) { |
| ((ConfigurableCronJob)job).setup(params, objects); |
| } |
| |
| return fireJob(jobrole, job); |
| } catch (final ServiceException se) { |
| getLogger().error("cannot fire job " + jobrole, se); |
| } finally { |
| manager.release(job); |
| } |
| |
| return false; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#fireJobAt(java.util.Date, java.lang.String, java.lang.Object) |
| */ |
| public void fireJobAt(final Date date, final String name, final Object job) |
| throws CascadingException { |
| fireJobAt(date, name, job, null, null); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#fireJobAt(java.util.Date, java.lang.String, java.lang.String) |
| */ |
| public void fireJobAt(final Date date, final String name, final String jobrole) |
| throws CascadingException { |
| fireJobAt(date, name, jobrole, null, null); |
| } |
| |
| /* (non-Javadoc) |
| * @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) |
| */ |
| public void fireJobAt(final Date date, final String name, final Object job, final Parameters params, |
| final Map objects) |
| throws CascadingException { |
| final JobDataMap jobDataMap = new JobDataMap(); |
| jobDataMap.put(DATA_MAP_OBJECT, job); |
| addJob(name, jobDataMap, date, true, params, objects); |
| } |
| |
| /* (non-Javadoc) |
| * @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) |
| */ |
| public void fireJobAt(final Date date, final String name, final String jobrole, final Parameters params, |
| final Map objects) |
| throws CascadingException { |
| final JobDataMap jobDataMap = new JobDataMap(); |
| jobDataMap.put(DATA_MAP_ROLE, jobrole); |
| addJob(name, jobDataMap, date, true, params, objects); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#removeJob(java.lang.String) |
| */ |
| public void removeJob(final String name) |
| throws NoSuchElementException { |
| try { |
| if (scheduler.deleteJob(name, DEFAULT_QUARTZ_JOB_GROUP)) { |
| getLogger().info("job " + name + " removed by request"); |
| } else { |
| getLogger().error("couldn't remove requested job " + name); |
| } |
| } catch (final SchedulerException se) { |
| getLogger().error("cannot remove job " + name, se); |
| throw new NoSuchElementException(se.getMessage()); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) |
| */ |
| public void service(final ServiceManager manager) |
| throws ServiceException { |
| this.manager = manager; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.activity.Startable#start() |
| */ |
| public void start() |
| throws Exception { |
| scheduler.start(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.activity.Startable#stop() |
| */ |
| public void stop() |
| throws Exception { |
| scheduler.standby(); |
| } |
| |
| /** |
| * Add a job to the scheduler |
| * |
| * @param name The name of the job to add |
| * @param jobDataMap The JobDataMap to use for this job |
| * @param date the date to schedule this job |
| * @param canRunConcurrently whether this job can be run concurrently |
| * @param params Additional Parameters to setup CronJob |
| * @param objects A Map with additional object to setup CronJob |
| * |
| * @throws CascadingException thrown in case of errors |
| */ |
| private void addJob(final String name, final JobDataMap jobDataMap, final Date date, |
| final boolean canRunConcurrently, final Parameters params, final Map objects) |
| throws CascadingException { |
| final SimpleTrigger trigger = new SimpleTrigger(name, DEFAULT_QUARTZ_JOB_GROUP, date); |
| addJob(name, jobDataMap, trigger, canRunConcurrently, params, objects); |
| } |
| |
| /** |
| * Add a job to the scheduler |
| * |
| * @param name The name of the job to add |
| * @param jobDataMap The JobDataMap to use for this job |
| * @param cronSpec a Cron time expression |
| * @param canRunConcurrently whether this job can be run concurrently |
| * @param params Additional Parameters to setup CronJob |
| * @param objects A Map with additional object to setup CronJob |
| * |
| * @throws CascadingException thrown in case of errors |
| */ |
| private void addJob(final String name, final JobDataMap jobDataMap, final String cronSpec, |
| final boolean canRunConcurrently, final Parameters params, final Map objects) |
| throws CascadingException { |
| final CronTrigger cronJobEntry = new CronTrigger(name, DEFAULT_QUARTZ_JOB_GROUP); |
| |
| try { |
| cronJobEntry.setCronExpression(cronSpec); |
| } catch (final ParseException pe) { |
| throw new CascadingException(pe.getMessage(), pe); |
| } |
| |
| addJob(name, jobDataMap, cronJobEntry, canRunConcurrently, params, objects); |
| } |
| |
| /** |
| * Add a job to the scheduler |
| * |
| * @param name The name of the job to add |
| * @param jobDataMap The JobDataMap to use for this job |
| * @param trigger a Trigger |
| * @param canRunConcurrently whether this job can be run concurrently |
| * @param params Additional Parameters to setup CronJob (might be null) |
| * @param objects A Map with additional object to setup CronJob (might be null) |
| * |
| * @throws CascadingException thrown in case of errors |
| */ |
| private void addJob(final String name, final JobDataMap jobDataMap, final Trigger trigger, |
| final boolean canRunConcurrently, final Parameters params, final Map objects) |
| throws CascadingException { |
| try { |
| final JobDetail jobdetail = scheduler.getJobDetail(name, DEFAULT_QUARTZ_JOB_GROUP); |
| if (jobdetail != null) { |
| removeJob(name); |
| } |
| } catch (final SchedulerException ignored) { |
| } |
| |
| initDataMap(jobDataMap, name, canRunConcurrently, params, objects); |
| |
| final JobDetail detail = createJobDetail(name, jobDataMap); |
| |
| if (getLogger().isInfoEnabled()) { |
| getLogger().info("Adding CronJob '" + trigger.getFullName() + "'"); |
| } |
| |
| try { |
| scheduler.scheduleJob(detail, trigger); |
| } catch (final SchedulerException se) { |
| throw new CascadingException(se.getMessage(), se); |
| } |
| |
| if (getLogger().isDebugEnabled()) { |
| if (trigger instanceof CronTrigger) { |
| getLogger().debug("Time schedule summary:\n" + ((CronTrigger)trigger).getExpressionSummary()); |
| } else { |
| getLogger().debug("Next scheduled time: " + trigger.getNextFireTime()); |
| } |
| } |
| } |
| |
| protected JobDataMap initDataMap(JobDataMap jobDataMap, String jobName, boolean concurent, |
| Parameters params, Map objects) { |
| jobDataMap.put(DATA_MAP_NAME, jobName); |
| jobDataMap.put(DATA_MAP_LOGGER, getLogger()); |
| jobDataMap.put(DATA_MAP_CONTEXT, this.context); |
| jobDataMap.put(DATA_MAP_MANAGER, this.manager); |
| jobDataMap.put(DATA_MAP_RUN_CONCURRENT, concurent? Boolean.TRUE: Boolean.FALSE); |
| if (null != params) { |
| jobDataMap.put(DATA_MAP_PARAMETERS, params); |
| } |
| if (null != objects) { |
| jobDataMap.put(DATA_MAP_OBJECTMAP, objects); |
| } |
| return jobDataMap; |
| } |
| |
| protected JobDetail createJobDetail(String name, JobDataMap jobDataMap) { |
| final JobDetail detail = new JobDetail(name, DEFAULT_QUARTZ_JOB_GROUP, QuartzJobExecutor.class); |
| detail.setJobDataMap(jobDataMap); |
| return detail; |
| } |
| |
| /** |
| * Create a QuartzThreadPool |
| * |
| * @param poolConfig Configuration element for the thread pool |
| * |
| * @return QuartzThreadPool |
| */ |
| private QuartzThreadPool createThreadPool(final Configuration poolConfig) |
| throws ServiceException { |
| final int queueSize = poolConfig.getChild("queue-size").getValueAsInteger(-1); |
| final int maxPoolSize = poolConfig.getChild("max-pool-size").getValueAsInteger(-1); |
| final int minPoolSize = poolConfig.getChild("min-pool-size").getValueAsInteger(-1); |
| final int keepAliveTimeMs = poolConfig.getChild("keep-alive-time-ms").getValueAsInteger(-1); |
| final String blockPolicy = poolConfig.getChild("block-policy").getValue(null); |
| m_shutdownGraceful = poolConfig.getChild("shutdown-graceful").getValueAsBoolean(true); |
| |
| final int shutdownWaitTimeMs = poolConfig.getChild("shutdown-wait-time-ms").getValueAsInteger(-1); |
| final RunnableManager runnableManager = (RunnableManager)this.manager.lookup(RunnableManager.ROLE); |
| this.executor = runnableManager.createPool(queueSize, |
| maxPoolSize, |
| minPoolSize, |
| Thread.NORM_PRIORITY, |
| false, // no daemon |
| keepAliveTimeMs, |
| blockPolicy, |
| m_shutdownGraceful, |
| shutdownWaitTimeMs); |
| final QuartzThreadPool pool = new QuartzThreadPool(this.executor); |
| return pool; |
| } |
| |
| /** |
| * Create the triggers |
| * |
| * @param triggers array of trigger configuration elements |
| * |
| * @throws ConfigurationException thrown in case of configuration failures |
| */ |
| private void createTriggers(final Configuration[] triggers) |
| throws ConfigurationException { |
| for (int i = 0; i < triggers.length; i++) { |
| String cron = triggers[i].getChild("cron").getValue(null); |
| |
| if (null == cron) { |
| final String seconds = triggers[i].getChild("seconds").getValue("0"); |
| final String minutes = triggers[i].getChild("minutes").getValue("*"); |
| final String hours = triggers[i].getChild("hours").getValue("*"); |
| final String days = triggers[i].getChild("days").getValue("*"); |
| final String months = triggers[i].getChild("months").getValue("*"); |
| final String weekdays = triggers[i].getChild("weekdays").getValue("?"); |
| final String years = triggers[i].getChild("years").getValue("*"); |
| cron = seconds + " " + minutes + " " + hours + " " + days + " " + months + " " + weekdays + " " + |
| years; |
| } |
| |
| try { |
| addJob(triggers[i].getAttribute("name"), triggers[i].getAttribute("target"), cron, |
| triggers[i].getAttributeAsBoolean("concurrent-runs", true)); |
| } catch (final CascadingException ce) { |
| throw new ConfigurationException("failed adding trigger to scheduler", ce); |
| } |
| } |
| } |
| |
| private JobStore createJobStore(String instanceName, String instanceID, final Configuration configuration) |
| throws ConfigurationException { |
| String type = configuration.getAttribute("type", "ram"); |
| if (type.equals("ram")) { |
| return new RAMJobStore(); |
| } |
| |
| JobStoreSupport store = null; |
| if (type.equals("tx")) { |
| store = new QuartzJobStoreTX(getLogger(), this.manager, this.context); |
| } else if (type.equals("cmt")) { |
| store = new QuartzJobStoreCMT(getLogger(), this.manager, this.context); |
| } else { |
| throw new ConfigurationException("Unknown store type: " + type); |
| } |
| |
| Configuration dsConfig = configuration.getChild("datasource", false); |
| if (dsConfig == null) { |
| throw new ConfigurationException("Store " + type + " requires datasource configuration."); |
| } |
| |
| String dsName = dsConfig.getValue(); |
| String dsType = dsConfig.getAttribute("provider", "jndi"); |
| |
| ConnectionProvider provider; |
| if (dsType.equals("jndi")) { |
| provider = new JNDIConnectionProvider(dsName, false); |
| } else if (dsType.equals("excalibur")) { |
| provider = new DataSourceComponentConnectionProvider(dsName, this.manager); |
| } else { |
| // assume class name |
| try { |
| provider = (ConnectionProvider)Class.forName(dsType).newInstance(); |
| } catch (Exception e) { |
| throw new ConfigurationException("Could not instantiate ConnectionProvider class " + dsType); |
| } |
| } |
| |
| store.setInstanceName(instanceName); |
| store.setInstanceId(instanceID); |
| store.setDataSource(dsType + ":" + dsName); |
| DBConnectionManager.getInstance().addConnectionProvider(dsType + ":" + dsName, provider); |
| |
| String delegate = configuration.getAttribute("delegate", null); |
| try { |
| if (delegate != null) { |
| store.setDriverDelegateClass(delegate); |
| } |
| } catch (InvalidConfigurationException e) { |
| throw new ConfigurationException("Could not instantiate DriverDelegate class " + delegate, e); |
| } |
| |
| return store; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.components.cron.JobScheduler#fireTarget(java.lang.Object) |
| */ |
| private boolean fireJob(final String name, final Object job) { |
| try { |
| if (job instanceof CronJob || job instanceof Job) { |
| JobDataMap jobDataMap = new JobDataMap(); |
| jobDataMap.put(DATA_MAP_OBJECT, job); |
| initDataMap(jobDataMap, name, true, null, null); |
| |
| final JobDetail detail = createJobDetail(name, jobDataMap); |
| |
| final Trigger trigger = new SimpleTrigger(name, DEFAULT_QUARTZ_JOB_GROUP); |
| |
| TriggerFiredBundle fireBundle = new TriggerFiredBundle(detail, trigger, null, false, null, null, null, null); |
| |
| final Job executor = createJobExecutor(); |
| final JobExecutionContext context = new JobExecutionContext(this.scheduler, fireBundle, executor); |
| |
| this.executor.execute(new Runnable() { |
| public void run() { |
| try { |
| executor.execute(context); |
| } catch (JobExecutionException e) { |
| getLogger().error("Job '" + job + "' died.", e); |
| } |
| } |
| }); |
| } else if (job instanceof Runnable) { |
| this.executor.execute((Runnable)job); |
| } else { |
| getLogger().error("Job named '" + name + "' is of invalid class: " + job.getClass().getName()); |
| return false; |
| } |
| |
| return true; |
| } catch (final InterruptedException ie) { |
| getLogger().error("job " + name + " interrupted", ie); |
| } |
| |
| return false; |
| } |
| |
| protected Job createJobExecutor() { |
| return new QuartzJobExecutor(); |
| } |
| |
| /** |
| * A QuartzThreadPool for the Quartz Scheduler based on Doug Leas concurrency utilities PooledExecutor |
| * |
| * @author <a href="mailto:giacomo@otego.com">Giacomo Pati</a> |
| * @version CVS $Id$ |
| */ |
| private static class QuartzThreadPool extends AbstractLogEnabled implements org.quartz.spi.ThreadPool { |
| /** Our executor thread pool */ |
| private ThreadPool executor; |
| |
| /** |
| * |
| */ |
| public QuartzThreadPool(final ThreadPool executor) { |
| super(); |
| this.executor = executor; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.quartz.spi.QuartzThreadPool#getPoolSize() |
| */ |
| public int getPoolSize() { |
| return this.executor.getMaximumPoolSize(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.quartz.spi.QuartzThreadPool#initialize() |
| */ |
| public void initialize() { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.quartz.spi.QuartzThreadPool#runInThread(java.lang.Runnable) |
| */ |
| public boolean runInThread(final Runnable job) { |
| try { |
| this.executor.execute(job); |
| } catch (final InterruptedException ie) { |
| getLogger().error("Cronjob failed", ie); |
| } |
| |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.quartz.spi.QuartzThreadPool#shutdown(boolean) |
| */ |
| public void shutdown(final boolean waitForJobsToComplete) { |
| this.executor.shutdown(); |
| } |
| } |
| } |