org.xerela.provider.scheduler.Scheduler.java Source code

Java tutorial

Introduction

Here is the source code for org.xerela.provider.scheduler.Scheduler.java

Source

/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is Ziptie Client Framework.
 * 
 * The Initial Developer of the Original Code is AlterPoint.
 * Portions created by AlterPoint are Copyright (C) 2006,
 * AlterPoint, Inc. All Rights Reserved.
 */

package org.xerela.provider.scheduler;

import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;

import javax.transaction.Transaction;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.quartz.CronTrigger;
import org.quartz.InterruptableJob;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.UnableToInterruptJobException;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.CronCalendar;
import org.xerela.provider.scheduler.internal.SchedulerActivator;
import org.xerela.zap.jta.TransactionElf;
import org.xerela.zap.security.ISecurityService;
import org.xerela.zap.security.IUserSession;

/**
 * Scheduler
 */
public class Scheduler implements IScheduler {
    private static final Logger LOGGER = Logger.getLogger(Scheduler.class);
    private static final String JOB_NAME_GROUP_OR_TYPE_IS_INVALID = "Job name, group, or type is invalid.";
    private static final String MSG_EXECUTION_NOT_ALLOWED = "Execution of job type %s not allowed for user '%s'";
    private static final String MSG_MODIFICATION_NOT_ALLOWED = "Modification of job type %s not allowed for user '%s'";
    private static final String USERNAME = "username";

    private org.quartz.Scheduler scheduler;
    private SchedulerEventProducer schedEventProducer;
    private Map<String, JobType> jobTypeByName;
    private Map<Class, String> jobNameByType;

    /**
     * Default constructor.
     *
     * @param props the configuration to use.
     * @throws SchedulerException thrown if the scheduler cannot be obtained from the factory and started
     */
    public Scheduler(Properties props) throws SchedulerException {
        jobTypeByName = new HashMap<String, JobType>();
        jobNameByType = new HashMap<Class, String>();

        try {
            StdSchedulerFactory factory = new StdSchedulerFactory(props);
            scheduler = factory.getScheduler();

            schedEventProducer = new SchedulerEventProducer();
            scheduler.addSchedulerListener(schedEventProducer);

            scheduler.addGlobalTriggerListener(new ExecutionTriggerListener());
            scheduler.addGlobalTriggerListener(new TriggerEventProducer());
        } catch (org.quartz.SchedulerException se) {
            se.printStackTrace();
            throw new SchedulerException(se.getMessage(), se);
        }
    }

    /** {@inheritDoc} */
    public List<JobType> getJobTypes() {
        ArrayList<JobType> list = new ArrayList<JobType>();
        list.addAll(jobTypeByName.values());
        return list;
    }

    /**
     * Return the Job type name to job class mapping that is maintained
     * by registerJobType and unregisterJobType.
     * 
     * @return an unmodifiable map of job type names to classes.
     */
    public Map<String, JobType> getJobTypeMapping() {
        return Collections.unmodifiableMap(jobTypeByName);
    }

    /**
     * Return the Job class to job type name mapping that is maintained
     * by registerJobType and unregisterJobType.
     *
     * @return an unmodifiable map of job class to job names.
     */
    public Map<Class, String> getJobClassMapping() {
        return Collections.unmodifiableMap(jobNameByType);
    }

    /** {@inheritDoc} */
    public void addJob(JobData jobData, boolean replace) {
        try {
            checkCudPermission(jobData);

            String jobName = jobData.getJobName();
            String jobGroup = jobData.getJobGroup();
            String jobDescription = jobData.getDescription();
            Map<String, String> jobParameters = jobData.getJobParameters();
            String jobTypeName = jobData.getJobType();
            boolean persistent = jobData.isPersistent();
            JobType jobType = jobTypeByName.get(jobTypeName);
            Class jobClass = jobType.getJobClass();

            if (jobName == null || jobGroup == null || jobClass == null) {
                throw new RuntimeException(JOB_NAME_GROUP_OR_TYPE_IS_INVALID);
            }

            JobDetail detail = new JobDetail(jobName, jobGroup, jobClass);
            detail.setDescription(jobDescription);
            detail.setVolatility(!persistent); // in the DB?
            detail.setDurability(persistent); // keep after orphaned (by triggers)

            JobDataMap dataMap = new JobDataMap(
                    jobParameters != null ? jobParameters : new HashMap<String, String>());

            // all triggers must be associated with the user that scheduled them.
            ISecurityService ss = SchedulerActivator.getSecurityService();
            if (ss != null && ss.getUserSession() != null && ss.getUserSession().getPrincipal() != null) {
                dataMap.put("job.creator", ss.getUserSession().getPrincipal().getName());
            }

            detail.setJobDataMap(dataMap);

            scheduler.addJob(detail, replace);

            schedEventProducer.jobAdded(detail);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public boolean deleteJob(String jobName, String jobGroup) {
        try {
            JobData jobData = getJob(jobName, jobGroup);
            checkCudPermission(jobData);

            boolean result = scheduler.deleteJob(jobName, jobGroup);

            if (result) {
                schedEventProducer.jobDeleted(jobName, jobGroup);
            }

            return result;
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public JobData getJob(String jobName, String jobGroup) {
        try {
            JobDetail jobDetail = scheduler.getJobDetail(jobName, jobGroup);
            if (jobDetail == null) {
                return null;
            }

            JobData jobData = new JobData(jobName, jobGroup);
            jobData.setJobType(jobNameByType.get(jobDetail.getJobClass()));
            jobData.setJobParameters(jobDetail.getJobDataMap());
            jobData.setDescription(jobDetail.getDescription());
            jobData.setPersistent(jobDetail.isDurable());

            return jobData;
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public JobMetadata[] getJobMetadataByGroup(String jobGroup) {
        try {
            ArrayList<JobMetadata> metadata = new ArrayList<JobMetadata>();

            String[] jobNames = scheduler.getJobNames(jobGroup);
            for (String jobName : jobNames) {
                JobDetail jobDetail = scheduler.getJobDetail(jobName, jobGroup);
                JobMetadata meta = new JobMetadata();
                meta.setJobGroup(jobGroup);
                meta.setJobName(jobName);
                meta.setJobType(jobNameByType.get(jobDetail.getJobClass()));
                meta.setJobDescription(jobDetail.getDescription());
                metadata.add(meta);
            }

            return metadata.toArray(new JobMetadata[0]);
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    // CHECKSTYLE:OFF
    public void scheduleJob(TriggerData triggerData) {
        try {
            String triggerName = triggerData.getTriggerName();
            String triggerGroup = triggerData.getTriggerGroup();
            String jobName = triggerData.getJobName();
            String jobGroup = triggerData.getJobGroup();
            String cronExpression = triggerData.getCronExpression();
            Date startTime = triggerData.getStartTime();
            Date endTime = triggerData.getEndTime();
            String timeZone = triggerData.getTimeZone();
            String filterName = triggerData.getFilterName();

            JobDetail jobDetail = scheduler.getJobDetail(jobName, jobGroup);

            CronTrigger trigger = new CronTrigger();
            trigger.setName(triggerName);
            trigger.setGroup(triggerGroup);
            trigger.setJobName(jobName);
            trigger.setJobGroup(jobGroup);
            trigger.setCronExpression(cronExpression);
            trigger.setCalendarName("".equals(filterName) ? null : filterName);
            trigger.setVolatility(jobDetail.isVolatile());

            Map<String, String> params = triggerData.getJobParameters();

            // NOTE: Do not create an empty map. There is a bug related to empty JobDataMaps and Derby for quartz 1.6.0.
            JobDataMap map = new JobDataMap(params == null ? new HashMap<String, String>() : params);

            // all triggers must be associated with the user that scheduled them.
            ISecurityService ss = SchedulerActivator.getSecurityService();
            if (ss != null && ss.getUserSession() != null && ss.getUserSession().getPrincipal() != null) {
                map.put(USERNAME, ss.getUserSession().getPrincipal().getName());
            }
            trigger.setJobDataMap(map);

            if (startTime != null) {
                trigger.setStartTime(startTime);
            }

            if (endTime != null) {
                trigger.setEndTime(endTime);
            }

            if (timeZone != null) {
                trigger.setTimeZone(TimeZone.getTimeZone(timeZone));
            }

            // Check if it already exists
            if (scheduler.getTrigger(triggerName, triggerGroup) == null) {
                scheduler.scheduleJob(trigger);
            } else {
                scheduler.rescheduleJob(triggerName, triggerGroup, trigger);
            }

            schedEventProducer.jobScheduled(trigger);
        } catch (ParseException e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        } catch (org.quartz.SchedulerException e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    // CHECKSTYLE:ON

    /** {@inheritDoc} */
    public boolean unscheduleJob(String triggerName, String groupName) {
        try {
            boolean result = scheduler.unscheduleJob(triggerName, groupName);
            if (result) {
                schedEventProducer.jobUnscheduled(triggerName, groupName);
            }
            return result;
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public String[] getJobGroupNames() {
        try {
            return scheduler.getJobGroupNames();
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public TriggerData getTrigger(String triggerName, String groupName) {
        try {
            TriggerData data = null;
            Trigger qtzTrigger = scheduler.getTrigger(triggerName, groupName);
            if (qtzTrigger instanceof CronTrigger) {
                CronTrigger cronTrigger = (CronTrigger) qtzTrigger;
                data = new TriggerData();
                data.setJobName(cronTrigger.getJobName());
                data.setJobGroup(cronTrigger.getJobGroup());
                data.setTriggerName(cronTrigger.getName());
                data.setTriggerGroup(cronTrigger.getGroup());
                data.setFilterName(cronTrigger.getCalendarName());
                data.setTimeZone(cronTrigger.getTimeZone().getID());
                data.setStartTime(cronTrigger.getStartTime());
                data.setEndTime(cronTrigger.getEndTime());
                data.setCronExpression(cronTrigger.getCronExpression());
                data.setPreviousFireTime(cronTrigger.getPreviousFireTime());
                data.setNextFireTime(cronTrigger.getNextFireTime());
                data.setFinalFireTime(cronTrigger.getFinalFireTime());
                data.setJobParameters(cronTrigger.getJobDataMap());

                int triggerState = scheduler.getTriggerState(triggerName, groupName);
                data.setPaused((triggerState & CronTrigger.STATE_PAUSED) != 0);
            }

            return data;
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public boolean interruptJob(String jobName, String groupName) {
        try {
            return scheduler.interrupt(jobName, groupName);
        } catch (UnableToInterruptJobException e) {
            LOGGER.warn(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public boolean interruptJob(int jobId) {
        try {
            boolean foundAndKilled = false;

            List currentlyExecutingJobs = scheduler.getCurrentlyExecutingJobs();
            for (Iterator iter = currentlyExecutingJobs.iterator(); iter.hasNext();) {
                JobExecutionContext currentJob = (JobExecutionContext) iter.next();
                Job jobInstance = currentJob.getJobInstance();
                ExecutionData data = (ExecutionData) currentJob.get(ExecutionData.class);
                data.setCanceled(true);
                save(data);

                if (jobId == data.getId() && jobInstance instanceof InterruptableJob) {
                    InterruptableJob job = (InterruptableJob) jobInstance;
                    foundAndKilled = true;
                    job.interrupt();

                    break;
                }
            }

            return foundAndKilled;
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public void pauseTrigger(String triggerName, String groupName) {
        try {
            if (triggerName == null) {
                scheduler.pauseTriggerGroup(groupName);
            } else if (groupName != null) {
                scheduler.pauseTrigger(triggerName, groupName);
            }
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public void resumeTrigger(String triggerName, String groupName) {
        try {
            if (triggerName == null) {
                scheduler.pauseTriggerGroup(groupName);
            } else if (groupName != null) {
                scheduler.resumeTrigger(triggerName, groupName);
            }
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public String[] getTriggerGroupNames() {
        try {
            return scheduler.getTriggerGroupNames();
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public String[] getTriggerNames(String groupName) {
        try {
            return scheduler.getTriggerNames(groupName);
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public List<TriggerData> getTriggersOfJob(String jobName, String jobGroup) {
        try {
            Trigger[] triggers = scheduler.getTriggersOfJob(jobName, jobGroup);

            List<TriggerData> result = new LinkedList<TriggerData>();
            for (Trigger trigger : triggers) {
                if (trigger instanceof CronTrigger) {
                    result.add(convertTrigger((CronTrigger) trigger));
                }
            }

            return result;
        } catch (org.quartz.SchedulerException e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public void pauseJob(String jobName, String jobGroup) {
        try {
            if (jobName == null) {
                scheduler.pauseJobGroup(jobGroup);
            } else if (jobGroup != null) {
                scheduler.pauseTrigger(jobName, jobGroup);
            }
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public void resumeJob(String jobName, String jobGroup) {
        try {
            if (jobName == null) {
                scheduler.resumeJobGroup(jobGroup);
            } else if (jobGroup != null) {
                scheduler.resumeTrigger(jobName, jobGroup);
            }
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public void addFilter(FilterData filterData, boolean replace, boolean updateTriggers) {
        try {
            String cronExpression = filterData.getCronExpression();
            String timeZone = filterData.getTimeZone();

            CronCalendar calendar = new CronCalendar(cronExpression);
            if (timeZone != null) {
                calendar.setTimeZone(TimeZone.getTimeZone(timeZone));
            }

            scheduler.addCalendar(filterData.getFilterName(), calendar, replace, updateTriggers);
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public boolean deleteFilter(String filterName) {
        try {
            return scheduler.deleteCalendar(filterName);
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public FilterData getFilter(String filterName) {
        try {
            CronCalendar calendar = (CronCalendar) scheduler.getCalendar(filterName);
            if (calendar != null) {
                FilterData filterData = new FilterData();
                filterData.setFilterName(filterName);
                filterData.setCronExpression(calendar.getCronExpression().getCronExpression());
                filterData.setTimeZone(calendar.getTimeZone().getID());
                return filterData;
            } else {
                return null;
            }
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public FilterData[] getAllFilters() {
        String[] names = getFilterNames();
        FilterData[] filters = new FilterData[names.length];
        for (int i = 0; i < names.length; i++) {
            filters[i] = getFilter(names[i]);
        }
        return filters;
    }

    /** {@inheritDoc} */
    public String[] getFilterNames() {
        try {
            return scheduler.getCalendarNames();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public ExecutionData runExistingJobNow(String jobName, String jobGroup) {
        try {
            JobDetail detail = scheduler.getJobDetail(jobName, jobGroup);
            if (detail == null) {
                return null;
            }

            return runNow(detail, false);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    /** {@inheritDoc} */
    public ExecutionData runNow(JobData jobData) {
        String jobName = jobData.getJobName();
        String jobGroup = jobData.getJobGroup();
        String jobDescription = jobData.getDescription();
        Map<String, String> jobParameters = jobData.getJobParameters();
        String jobTypeName = jobData.getJobType();
        JobType jobType = jobTypeByName.get(jobTypeName);
        Class jobClass = jobType.getJobClass();

        if (jobData.getJobName() == null || jobGroup == null || jobClass == null) {
            throw new RuntimeException(JOB_NAME_GROUP_OR_TYPE_IS_INVALID);
        }

        JobDetail detail = new JobDetail(jobName, jobGroup, jobClass);
        detail.setDurability(false);
        detail.setVolatility(true);
        detail.setDescription(jobDescription);
        JobDataMap dataMap = new JobDataMap(jobParameters != null ? jobParameters : new HashMap<String, String>());
        detail.setJobDataMap(dataMap);

        try {
            return runNow(detail, true);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    // CHECKSTYLE:OFF
    private ExecutionData runNow(JobDetail detail, boolean isUnsavedJob) throws org.quartz.SchedulerException {
        checkRunPermission(detail);

        int uniqueId;
        try {
            uniqueId = RunNowIdElf.getExecutionId();
        } catch (SQLException e) {
            throw new org.quartz.SchedulerException("Unable to obtain RunNow ID", e);
        }

        if (isUnsavedJob) {
            detail.setName(String.format("%s (Run now ID%d)", detail.getName(), uniqueId));
        }

        String jobName = detail.getName();

        SimpleTrigger trigger = new SimpleTrigger(jobName, String.format("%s : ID%d", detail.getGroup(), uniqueId));
        trigger.setJobName(jobName);
        trigger.setJobGroup(detail.getGroup());
        trigger.setVolatility(true);

        JobDataMap map = new JobDataMap();
        // NOTE: Do not create an empty map. There is a bug related to empty JobDataMaps and Derby for quartz 1.6.0.
        trigger.setJobDataMap(map);

        ISecurityService ss = SchedulerActivator.getSecurityService();
        if (ss != null && ss.getUserSession() != null && ss.getUserSession().getPrincipal() != null) {
            map.put(USERNAME, ss.getUserSession().getPrincipal().getName());
        }

        Transaction suspended = TransactionElf.suspend();
        TransactionElf.beginOrJoinTransaction();
        ExecutionData execution = createExecution(trigger, detail.getJobClass());
        save(execution);
        TransactionElf.commit();
        TransactionElf.resume(suspended);

        map.put("executionId", execution.getId());

        if (isUnsavedJob) {
            scheduler.scheduleJob(detail, trigger);
        } else {
            scheduler.scheduleJob(trigger);
        }

        return execution;
    }

    // CHECKSTYLE:ON

    /** {@inheritDoc} */
    public ExecutionData getExecutionDataById(int executionId) {
        Session session = SchedulerActivator.getSessionFactory().getCurrentSession();
        return (ExecutionData) session.get(ExecutionData.class, executionId);
    }

    /** {@inheritDoc} */
    public PageData getExecutionData(PageData pageData, String sortColumn, boolean descending) {
        Session session = SchedulerActivator.getSessionFactory().getCurrentSession();

        Criteria criteria = session.createCriteria(ExecutionData.class).add(Restrictions.isNotNull("startTime"))
                .setFirstResult(pageData.getOffset()).setMaxResults(pageData.getPageSize());

        if (pageData.getOffset() == 0) {
            // Set the total result size into the page data.
            criteria.setProjection(Projections.count("id"));

            Integer total = (Integer) criteria.uniqueResult();
            pageData.setTotal(total);

            criteria.setProjection(null);
        }

        if (sortColumn != null) {
            criteria.addOrder((descending ? Order.desc(sortColumn.trim()) : Order.asc(sortColumn.trim())));
        }

        List<ExecutionData> list = new ArrayList<ExecutionData>();
        ScrollableResults scroll = criteria.scroll(ScrollMode.SCROLL_INSENSITIVE);
        while (scroll.next()) {
            list.add((ExecutionData) scroll.get(0));
        }
        scroll.close();

        pageData.setExecutionData(list);

        return pageData;
    }

    /**
     * Start the Quartz Scheduler.
     *
     * @throws SchedulerException thrown if the Scheduler can't start.
     */
    public void start() throws SchedulerException {
        try {
            scheduler.start();
            LOGGER.debug("Scheduler Summary:\n" + scheduler.getMetaData().getSummary());
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Shutdown the Quartz Scheduler.
     *
     * @throws SchedulerException thrown if there was an error shutting down the Quartz Scheduler
     */
    public void shutdown() throws SchedulerException {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Get a reference to the 'raw' underlying Quartz scheduler.  This
     * is for Xerela internal server use only.
     *
     * @return a reference to the Quartz scheduler
     */
    public org.quartz.Scheduler getQuartzScheduler() {
        return scheduler;
    }

    /**
     * Register a new job type.
     *
     * @param name common name of the job type
     * @param clazz the job class
     * @param cudPermission the permission string for create/update/delete, or null
     * @param runPermission the permission string for run, or null
     */
    public void registerJobType(String name, Class<?> clazz, String cudPermission, String runPermission) {
        try {
            JobType jobType = new JobType();
            jobType.setTypeName(name);
            jobType.setJobClass(clazz);
            jobType.setCudPermission(cudPermission);
            jobType.setRunPermission(runPermission);

            jobTypeByName.put(name, jobType);
            jobNameByType.put(clazz, name);
        } catch (Exception e) {
            LOGGER.error("Unable to register job " + name + " of type " + clazz.getName());
        }
    }

    /**
     * Unregister a previously registered job type.
     * @param name the name of the job type
     * @param clazz the class of the job type
     * @throws SchedulerException if the supplied class does not match the class
     * previously registered for the supplied name.
     */
    public void unregisterJobType(String name, Class clazz) throws SchedulerException {
        JobType jobType = jobTypeByName.get(name);
        if (clazz.equals(jobType.getJobClass())) {
            jobTypeByName.remove(name);
            jobNameByType.remove(clazz);
        } else {
            throw new SchedulerException("unmatched job registration: " + name + " to " + clazz);
        }
    }

    private TriggerData convertTrigger(CronTrigger cronTrigger) throws org.quartz.SchedulerException {
        TriggerData data = new TriggerData();
        data.setJobName(cronTrigger.getJobName());
        data.setJobGroup(cronTrigger.getJobGroup());
        data.setTriggerName(cronTrigger.getName());
        data.setTriggerGroup(cronTrigger.getGroup());
        data.setFilterName(cronTrigger.getCalendarName());
        data.setTimeZone(cronTrigger.getTimeZone().getID());
        data.setStartTime(cronTrigger.getStartTime());
        data.setEndTime(cronTrigger.getEndTime());
        data.setCronExpression(cronTrigger.getCronExpression());
        data.setPreviousFireTime(cronTrigger.getPreviousFireTime());
        data.setNextFireTime(cronTrigger.getNextFireTime());
        data.setFinalFireTime(cronTrigger.getFinalFireTime());

        int triggerState = scheduler.getTriggerState(cronTrigger.getName(), cronTrigger.getGroup());
        data.setPaused((triggerState & CronTrigger.STATE_PAUSED) != 0);

        return data;
    }

    protected void save(ExecutionData execution) {
        if (!SchedulerActivator.isRAMStore()) {
            boolean success = false;
            boolean own = TransactionElf.beginOrJoinTransaction();
            try {
                Session session = SchedulerActivator.getSessionFactory().getCurrentSession();

                session.saveOrUpdate(execution);
                session.flush();

                success = true;
            } finally {
                if (success) {
                    if (own) {
                        TransactionElf.commit();
                    }
                } else {
                    TransactionElf.rollback();
                }
            }
        }
    }

    protected ExecutionData createExecution(Trigger trigger, Class<?> jobClass) {
        ExecutionData execution = new ExecutionData();
        execution.setJobGroup(trigger.getJobGroup());
        execution.setJobName(trigger.getJobName());
        execution.setJobClass(jobClass.getName());
        execution.setJobType(jobNameByType.get(jobClass));
        execution.setTriggerGroup(trigger.getGroup());
        execution.setTriggerName(trigger.getName());
        execution.setExecutor(trigger.getJobDataMap().getString(USERNAME));
        return execution;
    }

    private void checkRunPermission(JobDetail detail) throws org.quartz.SchedulerException {
        ISecurityService ss = SchedulerActivator.getSecurityService();
        if (ss != null && ISecureJob.class.isAssignableFrom(detail.getJobClass())) {
            try {
                ISecureJob secureJob = (ISecureJob) detail.getJobClass().newInstance();
                if (!secureJob.validateRunOperation(detail)) {
                    String jobType = jobNameByType.get(detail.getJobClass());
                    IUserSession userSession = ss.getUserSession();
                    throw new org.quartz.SchedulerException(
                            String.format(MSG_EXECUTION_NOT_ALLOWED, jobType, userSession.getPrincipal()));
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void checkCudPermission(JobData jobData) throws org.quartz.SchedulerException {
        String jobTypeName = jobData.getJobType();
        JobType jobType = jobTypeByName.get(jobTypeName);
        Class jobClass = jobType.getJobClass();

        String cudPermission = jobType.getCudPermission();
        ISecurityService ss = SchedulerActivator.getSecurityService();

        // only acquire the ZPrincipal if there are permissions to be checked.  there
        // are instances where a server-side component will call addJob(...) and not
        // require permission checks, and in such cases there is no ZPrincipal...
        if (ss != null && (cudPermission != null || ISecureJob.class.isAssignableFrom(jobClass))) {
            IUserSession userSession = ss.getUserSession();

            if (cudPermission != null && !userSession.checkHasPermission(cudPermission)) {
                throw new org.quartz.SchedulerException(
                        String.format(MSG_MODIFICATION_NOT_ALLOWED, jobType, userSession.getPrincipal()));
            }

            if (ISecureJob.class.isAssignableFrom(jobClass)) {
                try {
                    ISecureJob secureJob = (ISecureJob) jobClass.newInstance();
                    if (!secureJob.validateCudOperation(jobData)) {
                        throw new org.quartz.SchedulerException(
                                String.format(MSG_MODIFICATION_NOT_ALLOWED, jobType, userSession.getPrincipal()));
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}