Package org.exist.scheduler.impl

Source Code of org.exist.scheduler.impl.QuartzSchedulerImpl

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-2011 The eXist-db team
*  http://exist-db.org
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program; if not, write to the Free Software Foundation
*  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*  $Id$
*/
package org.exist.scheduler.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.Logger;
import org.exist.EXistException;
import org.exist.scheduler.*;
import org.exist.security.Subject;
import org.exist.storage.BrokerPool;
import org.exist.storage.SystemTask;
import org.exist.util.Configuration;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import org.quartz.Job;
import static org.quartz.JobBuilder.newJob;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import static org.quartz.TriggerBuilder.newTrigger;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;

/**
* A Scheduler to trigger Startup, System and User defined jobs.
*
* @author  Adam Retter <adam@existsolutions.com>
* @author  Andrzej Taramina <andrzej@chaeron.com>
*/
public class QuartzSchedulerImpl implements Scheduler {

    private final static Logger LOG = Logger.getLogger(QuartzSchedulerImpl.class); //Logger

    //the scheduler
    private final org.quartz.Scheduler scheduler;

    private final BrokerPool brokerPool;
    private final Configuration config;

    /**
     * Create and Start a new Scheduler.
     *
     * @param   brokerpool  The broker pool for which this scheduler is intended
     * @param   config      DOCUMENT ME!
     *
     * @throws  EXistException  DOCUMENT ME!
     */
    public QuartzSchedulerImpl(final BrokerPool brokerpool, final Configuration config) throws EXistException {
        this.brokerPool = brokerpool;
        this.config = config;
        try {
            final SchedulerFactory schedulerFactory = new StdSchedulerFactory(getQuartzProperties());
            scheduler = schedulerFactory.getScheduler();
        } catch(final SchedulerException se) {
            throw(new EXistException("Unable to create Scheduler: " + se.getMessage(), se));
        }
    }

    private final static Properties defaultQuartzProperties = new Properties();

    static {
        defaultQuartzProperties.setProperty("org.quartz.scheduler.instanceName", "DefaultQuartzScheduler");
        defaultQuartzProperties.setProperty("org.quartz.scheduler.rmi.export", "false");
        defaultQuartzProperties.setProperty("org.quartz.scheduler.rmi.proxy", "false");
        defaultQuartzProperties.setProperty("org.quartz.scheduler.wrapJobExecutionInUserTransaction", "false");
        defaultQuartzProperties.setProperty("org.quartz.scheduler.skipUpdateCheck", "true");
        defaultQuartzProperties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        defaultQuartzProperties.setProperty("org.quartz.threadPool.threadCount", "4");
        defaultQuartzProperties.setProperty("org.quartz.threadPool.threadPriority", "5");
        defaultQuartzProperties.setProperty("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true");
        defaultQuartzProperties.setProperty("org.quartz.jobStore.class", "org.quartz.simpl.RAMJobStore");
        defaultQuartzProperties.setProperty("org.quartz.jobStore.misfireThreshold", "60000");
    }

    private Properties getQuartzProperties() {
        //try and load the properties for quartz
        InputStream is = null;
        final Properties properties = new Properties();
        try {
            is = this.getClass().getResourceAsStream("quartz.properties");
            if(is != null) {
                properties.load(is);
                LOG.info("Succesfully loaded quartz.properties");
            } else {
                LOG.warn("Could not load quartz.properties, will use defaults.");
            }
        } catch(final IOException ioe) {
            LOG.warn("Could not load quartz.properties, will defaults. " + ioe.getMessage(), ioe);
        } finally {
            if(is != null) {
                try {
                    is.close();
                } catch(final IOException ioe) {
                    //Nothing to do
                }
            }
        }
        if (properties == null || properties.size() == 0) {
            LOG.warn("Using default properties for Quartz scheduler");
            properties.putAll(defaultQuartzProperties);
        }
        if (!properties.containsKey(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME)) {
            properties.setProperty(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME,
                brokerPool.getId() + "_QuartzScheduler");
        }
        return properties;
    }

    protected org.quartz.Scheduler getScheduler() {
        return scheduler;
    }

    @Override
    public void run() {
        try {
            setupConfiguredJobs();
            getScheduler().start();
        } catch(final SchedulerException se) {
            LOG.error("Unable to start the Scheduler: " + se.getMessage(), se);
        }
    }

    /**
     * Shutdown the running Scheduler.
     *
     * <p>Asynchronous method. use isShutdown() to determine if the Scheduler has Shutdown</p>
     *
     * @param  waitForJobsToComplete Should we wait for currently executing jobs
     * to complete before shutting down?
     */
    @Override
    public void shutdown(final boolean waitForJobsToComplete) {
        try {
            getScheduler().shutdown(waitForJobsToComplete);
        } catch(final SchedulerException se) {
            LOG.warn("Unable to shutdown the Scheduler:" + se.getMessage(), se);
        }
    }

    @Override
    public boolean isShutdown() {
        boolean isShutdown = false;
        try {
            isShutdown = getScheduler().isShutdown();
        } catch(final SchedulerException se) {
            LOG.warn("Unable to determine the status of the Scheuler: " + se.getMessage(), se);
        }
        return isShutdown;
    }

    /**
     * Create Periodic Job
     *
     * @param   period  The period, in milliseconds.
     * @param   job     The job to trigger after each period
     * @param   delay   <= 0, start now, otherwise start in specified number of milliseconds
     *
     * @return  true if the job was successfully scheduled, false otherwise
     */
    @Override
    public boolean createPeriodicJob(final long period, final JobDescription job, final long delay) {
        return createPeriodicJob(period, job, delay, null, SimpleTrigger.REPEAT_INDEFINITELY);
    }

    /**
     * Create Periodic Job
     *
     * @param   period  The period, in milliseconds.
     * @param   job     The job to trigger after each period
     * @param   delay   <= 0, start now, otherwise start in specified number of milliseconds
     * @param   params  Any parameters to pass to the job
     *
     * @return  true if the job was successfully scheduled, false otherwise
     */
    @Override
    public boolean createPeriodicJob(final long period, final JobDescription job, final long delay, final Properties params) {
        return createPeriodicJob(period, job, delay, params, SimpleTrigger.REPEAT_INDEFINITELY);
    }

    /**
     * Create Periodic Job
     *
     * @param   period       The period, in milliseconds.
     * @param   job          The job to trigger after each period
     * @param   delay        <= 0, start now, otherwise start in specified number of milliseconds
     * @param   params       Any parameters to pass to the job
     * @param   repeatCount  Number of times to repeat this job.
     *
     * @return  true if the job was successfully scheduled, false otherwise
     */
    @Override
    public boolean createPeriodicJob(final long period, final JobDescription job, final long delay, final Properties params, final int repeatCount) {
        return createPeriodicJob(period, job, delay, params, repeatCount, true);
    }

    /**
     * Create Periodic Job
     *
     * @param   period       The period, in milliseconds.
     * @param   job          The job to trigger after each period
     * @param   delay        <= 0, start now, otherwise start in specified number of milliseconds
     * @param   params       Any parameters to pass to the job
     * @param   repeatCount  Number of times to repeat this job.
     * @param   unschedule   Unschedule job on XPathException?
     *
     * @return  true if the job was successfully scheduled, false otherwise
     */
    @Override
    public boolean createPeriodicJob(final long period, final JobDescription job, final long delay, final Properties params, final int repeatCount, final boolean unschedule) {
        //Create the job details
        final JobDetail jobDetail = newJob((Class<? extends Job>)job.getClass())
            .withIdentity(job.getName(), job.getGroup()).build();
       
        //Setup the job's data map
        final JobDataMap jobDataMap = jobDetail.getJobDataMap();
        setupJobDataMap(job, jobDataMap, params, unschedule);
       
        //when should the trigger start
        final Date triggerStart;
        if(delay <= 0) {
            //start now
            triggerStart = new Date();
        } else {
            //start after period
            final Calendar start = Calendar.getInstance();
            start.add(Calendar.MILLISECOND, (int)delay);
            triggerStart = start.getTime();
        }
       
        //setup a trigger for the job, millisecond based
        final Trigger trigger = newTrigger()
        .withIdentity(job.getName() + " Trigger", job.getGroup())
        .startAt(triggerStart)
        .withSchedule(simpleSchedule()
            .withIntervalInMilliseconds(period)
            .withRepeatCount(repeatCount)
        ).build();
       
        //schedule the job
        try {
            getScheduler().scheduleJob(jobDetail, trigger);
        } catch(final SchedulerException se) {
            //Failed to schedule Job
            LOG.error("Failed to schedule periodic job '" + job.getName() + "': " + se.getMessage(), se);
            return false ;
        }
        //Successfully scheduled Job
        return true;
    }

    /**
     * Create Cron Job
     *
     * @param   cronExpression  The Cron scheduling expression
     * @param   job             The job to trigger after each period
     *
     * @return  true if the job was successfully scheduled, false otherwise
     */
    @Override
    public boolean createCronJob(final String cronExpression, final JobDescription job) {
        return createCronJob(cronExpression, job, null);
    }

    /**
     * Create Cron Job
     *
     * @param   cronExpression  The Cron scheduling expression
     * @param   job             The job to trigger after each period
     * @param   params          Any parameters to pass to the job
     *
     * @return  true if the job was successfully scheduled, false otherwise
     */
    @Override
    public boolean createCronJob(final String cronExpression, final JobDescription job, final Properties params) {
        return createCronJob(cronExpression, job, params, true);
    }

    /**
     * Create Cron Job
     *
     * @param   cronExpression  The Cron scheduling expression
     * @param   job             The job to trigger after each period
     * @param   params          Any parameters to pass to the job
     * @param   unschedule   Unschedule job on XPathException?.
     *
     * @return  true if the job was successfully scheduled, false otherwise
     */
    @Override
    public boolean createCronJob(final String cronExpression, final JobDescription job, final Properties params, final boolean unschedule) {
        //Create the job details
        final JobDetail jobDetail = newJob((Class<? extends Job>)job.getClass())
            .withIdentity(job.getName(), job.getGroup()).build();
       
        //Setup the job's data map
        final JobDataMap jobDataMap = jobDetail.getJobDataMap();
        setupJobDataMap(job, jobDataMap, params, unschedule);
       
        try {
            //setup a trigger for the job, Cron based
            final Trigger trigger = newTrigger()
            .withIdentity(job.getName() + " Trigger", job.getGroup())
            .withSchedule(cronSchedule(cronExpression)).build();
           
            //schedule the job
            getScheduler().scheduleJob(jobDetail, trigger);
        } catch(final SchedulerException se) {
            //Failed to schedule Job
            LOG.error("Failed to schedule cron job '" + job.getName() + "': " + se.getMessage(), se);
            return false;
        }
        //Successfully scheduled Job
        return true;
    }

    /**
     * Removes a Job from the Scheduler.
     *
     * @param   jobName   The name of the Job
     * @param   jobGroup  The group that the Job was Scheduled in
     *
     * @return  true if the job was deleted, false otherwise
     */
    @Override
    public boolean deleteJob(final String jobName, final String jobGroup) {
        boolean deletedJob = false;
        try {
            deletedJob = getScheduler().deleteJob(new JobKey(jobName, jobGroup));
        } catch(final SchedulerException se) {
            LOG.error("Failed to delete job '" + jobName + "': " + se.getMessage(), se);
        }
        return deletedJob;
    }

    /**
     * Pauses a Job with the Scheduler.
     *
     * @param   jobName   The name of the Job
     * @param   jobGroup  The group that the Job was Scheduled in
     *
     * @return  true if the job was paused, false otherwise
     */
    @Override
    public boolean pauseJob(final String jobName, final String jobGroup) {
        boolean pausedJob = false;
        try {
            getScheduler().pauseJob(new JobKey(jobName, jobGroup));
            pausedJob = true;
        } catch(final SchedulerException se) {
            LOG.error( "Failed to pause job '" + jobName + "': " + se.getMessage(), se);
        }
        return pausedJob;
    }

    /**
     * Resume a Job with the Scheduler.
     *
     * @param   jobName   The name of the Job
     * @param   jobGroup  The group that the Job was Scheduled in
     *
     * @return  true if the job was resumed, false otherwise
     */
    @Override
    public boolean resumeJob(final String jobName, final String jobGroup) {
        boolean resumedJob = false;
        try {
            getScheduler().resumeJob(new JobKey(jobName, jobGroup));
            resumedJob = true;
        } catch(final SchedulerException se) {
            LOG.error("Failed to resume job '" + jobName + "': " + se.getMessage(), se);
        }
        return resumedJob;
    }

    /**
     * Gets the names of the Job groups.
     *
     * @return  String array of the Job group names
     */
    @Override
    public List<String> getJobGroupNames() {
        final List<String> jobNames = new ArrayList<String>();
        try {
            jobNames.addAll(getScheduler().getJobGroupNames());
        } catch(final SchedulerException se) {
            LOG.error( "Failed to get job group names: " + se.getMessage(), se );
        }
        return jobNames;
    }

    /**
     * Gets information about currently Scheduled Jobs.
     *
     * @return  An array of ScheduledJobInfo
     */
    @Override
    public List<ScheduledJobInfo> getScheduledJobs() {
        final List<ScheduledJobInfo> scheduledJobs = new ArrayList<ScheduledJobInfo>();
        try {
            //get the trigger groups
            for(final String triggerGroupName : getScheduler().getTriggerGroupNames()) {
                //get the trigger names for the trigger group
                for(final TriggerKey triggerKey : getScheduler().getTriggerKeys(GroupMatcher.triggerGroupEquals(triggerGroupName))) {
                    //add information about the job to the result
                    scheduledJobs.add(new ScheduledJobInfo(getScheduler(), getScheduler().getTrigger(triggerKey)));
                }
            }
        } catch(final SchedulerException se) {
            LOG.error("Failed to get scheduled jobs: " + se.getMessage(), se);
        }
        return scheduledJobs;
    }

    /**
     * Gets information about currently Executing Jobs.
     *
     * @return  An array of ScheduledJobInfo
     */
    @Override
    public ScheduledJobInfo[] getExecutingJobs() {
        ScheduledJobInfo result[] = null;
        try {
            final List<ScheduledJobInfo> jobs = new ArrayList<ScheduledJobInfo>();
            for(final JobExecutionContext jobExecutionCtx : (List<JobExecutionContext>)getScheduler().getCurrentlyExecutingJobs()) {
                jobs.add(new ScheduledJobInfo(getScheduler(), jobExecutionCtx.getTrigger()));
            }
            result = new ScheduledJobInfo[jobs.size()];
            jobs.toArray(result);
        } catch(final SchedulerException se) {
            LOG.error("Failed to get executing jobs: " + se.getMessage(), se);
        }
        return result;
    }

    /**
     * Set's up all the jobs that are listed in conf.xml and loaded through org.exist.util.Configuration.
     */
    @Override
    public void setupConfiguredJobs() {
        final JobConfig[] jobList = (JobConfig[])config.getProperty(JobConfig.PROPERTY_SCHEDULER_JOBS);
       
        if(jobList == null) {
            return;
        }
       
        for(final JobConfig jobConfig : jobList) {
            JobDescription job = null;
            if(jobConfig.getResourceName().startsWith("/db/") || jobConfig.getResourceName().indexOf(':') > 0) {
                if(jobConfig.getType().equals(JobType.SYSTEM)) {
                    LOG.error("System jobs may only be written in Java");
                } else {
                    //create an XQuery job
                    final Subject guestUser = brokerPool.getSecurityManager().getGuestSubject();
                    job = new UserXQueryJob(jobConfig.getJobName(), jobConfig.getResourceName(), guestUser);
                    try {
                        // check if a job with the same name is already registered
                        if(getScheduler().getJobDetail(new JobKey(job.getName(), UserJob.JOB_GROUP)) != null) {
                            // yes, try to make the job's name unique
                            ((UserXQueryJob)job).setName(job.getName() + job.hashCode());
                        }
                       
                    } catch(final SchedulerException e) {
                        LOG.error("Unable to set job name: " + e.getMessage(), e);
                    }
                }
               
            } else {
                //create a Java job
                try {
                    final Class<?> jobClass = Class.forName(jobConfig.getResourceName());
                    final Object jobObject = jobClass.newInstance();
                    if(jobConfig.getType().equals(JobType.SYSTEM)) {
                        if(jobObject instanceof SystemTask) {
                            final SystemTask task = (SystemTask)jobObject;
                            task.configure(config, jobConfig.getParameters());
                            job = new SystemTaskJobImpl(jobConfig.getJobName(), task);
                        } else {
                            LOG.error("System jobs must extend SystemTask");
                            // throw exception? will be handled nicely
                        }
                       
                    } else {
                        if(jobObject instanceof JobDescription) {
                            job = (JobDescription)jobObject;
                            if(jobConfig.getJobName() != null) {
                                job.setName(jobConfig.getJobName());
                            }
                        } else {
                            LOG.error("Startup job " + jobConfig.getJobName() +"  must extend org.exist.scheduler.StartupJob");
                            // throw exception? will be handled nicely
                        }
                    }
                   
                } catch(final Exception e) { // Throwable?
                    LOG.error("Unable to schedule '" + jobConfig.getType() + "' job " + jobConfig.getResourceName() + ": " + e.getMessage(), e);
                }
            }
           
            //if there is a job, schedule it
            if(job != null) {
                //timed job
                //trigger is Cron or period?
                if(jobConfig.getSchedule().indexOf(' ') > -1) {
                    //schedule job with Cron trigger
                    createCronJob(jobConfig.getSchedule(), job, jobConfig.getParameters());
                } else {
                    //schedule job with periodic trigger
                    createPeriodicJob(Long.parseLong(jobConfig.getSchedule()), job, jobConfig.getDelay(), jobConfig.getParameters(), jobConfig.getRepeat(), jobConfig.unscheduleOnException());
                }
            }
        }
    }

    /**
     * Sets up the Job's Data Map.
     *
     * @param  job         The Job
     * @param  jobDataMap  The Job's Data Map
     * @param  params      Any parameters for the job
     */
    private void setupJobDataMap(final JobDescription job, final JobDataMap jobDataMap, final Properties params, final boolean unschedule) {
        //if this is a system job, store the BrokerPool in the job's data map
        jobDataMap.put("brokerpool", brokerPool);
        //if this is a system task job, store the SystemTask in the job's data map
        if(job instanceof SystemTaskJobImpl) {
            jobDataMap.put("systemtask", ((SystemTaskJobImpl)job).getSystemTask());
        }
        //if this is a users XQuery job, store the XQuery resource and user in the job's data map
        if(job instanceof UserXQueryJob) {
            jobDataMap.put("xqueryresource", ((UserXQueryJob)job).getXQueryResource());
            jobDataMap.put("user", ((UserXQueryJob)job).getUser());
        }
        //copy any parameters into the job's data map
        if(params != null) {
            jobDataMap.put("params", params);
        }
        //Store the value of the unschedule setting
        jobDataMap.put("unschedule", Boolean.valueOf(unschedule));
    }
}
TOP

Related Classes of org.exist.scheduler.impl.QuartzSchedulerImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.