package org.ovirt.engine.core.utils.timer; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.PreDestroy; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.ejb3.annotation.Management; import org.jboss.ejb3.annotation.Service; import org.ovirt.engine.core.utils.ejb.BeanProxyType; import org.ovirt.engine.core.utils.ejb.BeanType; import org.ovirt.engine.core.utils.ejb.EjbUtils; import org.quartz.CronTrigger; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.SimpleTrigger; import org.quartz.Trigger; import org.quartz.impl.StdSchedulerFactory; @Service(name = "Scheduler") @Management(SchedulerUtil.class) @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) public class SchedulerUtilQuartzImpl implements SchedulerUtil { // consts public static final String RUNNABLE_INSTANCE = "runnable.instance"; public static final String RUN_METHOD_NAME = "method.name"; public static final String RUN_METHOD_PARAM_TYPE = "method.paramType"; public static final String RUN_METHOD_PARAM = "method.param"; public static final String FIXED_DELAY_VALUE = "fixedDelayValue"; public static final String FIXED_DELAY_TIME_UNIT = "fixedDelayTimeUnit"; private static final String TRIGGER_PREFIX = "trigger"; // members private final Log log = LogFactory.getLog(SchedulerUtilQuartzImpl.class); private Scheduler sched; private final AtomicLong sequenceNumber = new AtomicLong(Long.MIN_VALUE); /** * This method is called upon the bean creation as part * of the management Service bean lifecycle. */ public void create(){ setup(); } /* * retrieving the quartz scheduler from the factory. */ public void setup() { try { SchedulerFactory sf = new StdSchedulerFactory(); sched = sf.getScheduler(); sched.start(); sched.addJobListener(new FixedDelayJobListener(this)); } catch (SchedulerException se) { log.error("there is a problem with the underlying Scheduler.", se); } } @PreDestroy public void teardown() { try { if (sched != null) { sched.shutdown(); } } catch (SchedulerException e) { log.error("Failed to shutdown Quartz service", e); } } /** * Returns the single instance of this Class. * * @return a SchedulerUtil instance */ public static SchedulerUtil getInstance() { return EjbUtils.findBean(BeanType.SCHEDULER, BeanProxyType.LOCAL); } /** * schedules a fixed delay job. * * @param instance * - the instance to activate the method on timeout * @param methodName * - the name of the method to activate on the instance * @param inputTypes * - the method input types * @param inputParams * - the method input parameters * @param initialDelay * - the initial delay before the first activation * @param taskDelay * - the delay between jobs * @param timeUnit * - the unit of time used for initialDelay and taskDelay. * @return the scheduled job id */ @SuppressWarnings("unchecked") @Override public String scheduleAFixedDelayJob(Object instance, String methodName, Class[] inputTypes, Object[] inputParams, long initialDelay, long taskDelay, TimeUnit timeUnit) { String jobName = generateUniqueNameForInstance(instance, methodName); JobDetail job = new JobDetail(jobName, Scheduler.DEFAULT_GROUP, JobWrapper.class); job.addJobListener(FixedDelayJobListener.FIXED_JOB_LISTENER_NAME); JobDataMap data = job.getJobDataMap(); data.put(RUNNABLE_INSTANCE, instance); data.put(RUN_METHOD_NAME, methodName); data.put(RUN_METHOD_PARAM_TYPE, inputTypes); data.put(RUN_METHOD_PARAM, inputParams); data.put(FIXED_DELAY_VALUE, taskDelay); data.put(FIXED_DELAY_TIME_UNIT, timeUnit); Date runTime = getFutureDate(initialDelay, timeUnit); String triggerName = generateUniqueNameForInstance(instance, TRIGGER_PREFIX); SimpleTrigger trigger = new SimpleTrigger(triggerName, Scheduler.DEFAULT_GROUP, runTime); try { sched.scheduleJob(job, trigger); } catch (SchedulerException se) { log.error("failed to schedule job", se); } return jobName; } /** * schedules a one time job. * * @param instance * - the instance to activate the method on timeout * @param methodName * - the name of the method to activate on the instance * @param inputTypes * - the method input types * @param inputParams * - the method input parameters * @param initialDelay * - the initial delay before the job activation * @param timeUnit * - the unit of time used for initialDelay and taskDelay. * @return the scheduled job id */ @SuppressWarnings("unchecked") @Override public String scheduleAOneTimeJob(Object instance, String methodName, Class[] inputTypes, Object[] inputParams, long initialDelay, TimeUnit timeUnit) { String jobName = generateUniqueNameForInstance(instance, methodName); JobDetail job = new JobDetail(jobName, Scheduler.DEFAULT_GROUP, JobWrapper.class); JobDataMap data = job.getJobDataMap(); data.put(RUNNABLE_INSTANCE, instance); data.put(RUN_METHOD_NAME, methodName); data.put(RUN_METHOD_PARAM, inputParams); data.put(RUN_METHOD_PARAM_TYPE, inputTypes); Date runTime = getFutureDate(initialDelay, timeUnit); String triggerName = generateUniqueNameForInstance(instance, TRIGGER_PREFIX); SimpleTrigger trigger = new SimpleTrigger(triggerName, Scheduler.DEFAULT_GROUP, runTime); try { sched.scheduleJob(job, trigger); } catch (SchedulerException se) { log.error("failed to schedule job", se); } return jobName; } /** * schedules a cron job. * * @param instance * - the instance to activate the method on timeout * @param methodName * - the name of the method to activate on the instance * @param inputTypes * - the method input types * @param inputParams * - the method input parameters * @param cronExpression * - cron expression to run this job * @return the scheduled job id */ @SuppressWarnings("unchecked") @Override public String scheduleACronJob(Object instance, String methodName, Class[] inputTypes, Object[] inputParams, String cronExpression) { String jobName = generateUniqueNameForInstance(instance, methodName); JobDetail job = new JobDetail(jobName, Scheduler.DEFAULT_GROUP, JobWrapper.class); JobDataMap data = job.getJobDataMap(); data.put(RUNNABLE_INSTANCE, instance); data.put(RUN_METHOD_NAME, methodName); data.put(RUN_METHOD_PARAM, inputParams); data.put(RUN_METHOD_PARAM_TYPE, inputTypes); try { String triggerName = generateUniqueNameForInstance(instance, TRIGGER_PREFIX); Trigger trigger = new CronTrigger(triggerName, Scheduler.DEFAULT_GROUP, cronExpression); sched.scheduleJob(job, trigger); } catch (Exception se) { log.error("failed to schedule job", se); } return jobName; } /** * reschedule the job associated with the given old trigger with the new * trigger. * * @param oldTriggerName * - the name of the trigger to remove. * @param oldTriggerGroup * - the group of the trigger to remove. * @param newTrigger * - the new Trigger to associate the job with */ public void rescheduleAJob(String oldTriggerName, String oldTriggerGroup, Trigger newTrigger) { try { sched.rescheduleJob(oldTriggerName, oldTriggerGroup, newTrigger); } catch (SchedulerException se) { log.error("failed to reschedule the job", se); } } /** * pauses a job with the given jobId assuming the job is in the default * quartz group * * @param jobId */ @Override public void pauseJob(String jobId) { try { sched.pauseJob(jobId, Scheduler.DEFAULT_GROUP); } catch (SchedulerException se) { log.error("failed to pause a job with id=" + jobId, se); } } /** * Delete the identified Job from the Scheduler * * @param jobId * - the id of the job to delete */ @Override public void deleteJob(String jobId) { try { sched.deleteJob(jobId, Scheduler.DEFAULT_GROUP); } catch (SchedulerException se) { log.error("failed to delete a job with id=" + jobId, se); } } /** * resume a job with the given jobId assuming the job is in the default * quartz group * * @param jobId */ @Override public void resumeJob(String jobId) { try { sched.resumeJob(jobId, Scheduler.DEFAULT_GROUP); } catch (SchedulerException se) { log.error("failed to pause a job with id=" + jobId, se); } } /** * Halts the Scheduler, and cleans up all resources associated with the * Scheduler. The scheduler cannot be re-started. * * @see org.quartz.Scheduler#shutdown(boolean waitForJobsToComplete) */ @Override public void shutDown() { try { sched.shutdown(true); } catch (SchedulerException se) { log.error("failed to shut down the scheduler", se); } } /** * @return the quartz scheduler wrapped by this SchedulerUtil */ public Scheduler getRawScheduler() { return sched; } /* * returns a future date with the given delay. the delay is being calculated * according to the given Time units */ public static Date getFutureDate(long delay, TimeUnit timeUnit) { if (delay > 0) { return new Date(new Date().getTime() + TimeUnit.MILLISECONDS.convert(delay, timeUnit)); } else { return new Date(); } } /* * generate a unique name for the given instance, using a sequence number. */ private String generateUniqueNameForInstance(Object instance, String nestedName) { String name = instance.getClass().getName() + "." + nestedName + "#" + sequenceNumber.incrementAndGet(); return name; } }