package org.ovirt.engine.core.utils.timer; import static org.quartz.JobBuilder.newJob; import static org.quartz.impl.matchers.GroupMatcher.jobGroupEquals; import java.io.IOException; import java.util.Date; import java.util.Properties; import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.inject.Singleton; import org.apache.commons.lang.ClassUtils; import org.ovirt.engine.core.utils.ResourceUtils; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.impl.StdSchedulerFactory; @Singleton public class DBSchedulerUtilQuartzImpl extends SchedulerUtilBaseImpl implements SchedulerUtil { @Override @PostConstruct public void create() { setup(); } /* * retrieving the quartz scheduler from the factory. */ public void setup() { final String QUARTZ_DB_PROPERTIES = "ovirt-db-scheduler.properties"; Properties props = null; try { props = ResourceUtils.loadProperties(SchedulerUtil.class, QUARTZ_DB_PROPERTIES); } catch (IOException exception) { throw new IllegalStateException( "Can't load properties from resource \"" + QUARTZ_DB_PROPERTIES + "\".", exception); } setup(props); } public void setup(Properties props) { try { SchedulerFactory sf = new StdSchedulerFactory(props); sched = sf.getScheduler(); if (sched != null) { sched.start(); sched.getListenerManager().addJobListener(new FixedDelayJobListener(this), jobGroupEquals(Scheduler.DEFAULT_GROUP)); } else { log.error("there is a problem with the underlying Scheduler: null returned"); } } catch (SchedulerException se) { log.error("there is a problem with the underlying Scheduler: {}", se.getMessage()); log.debug("Exception", se); } } @PreDestroy public void teardown() { super.shutDown(); } /** * To avoid data serialization issues for jobdata that is persisted in the database inputParams should be of * primitive or String type */ @Override public String scheduleAFixedDelayJob(Object instance, String methodName, Class<?>[] inputTypes, Object[] inputParams, long initialDelay, long taskDelay, TimeUnit timeUnit) { if (!validate(instance, inputTypes)) { return null; } return super.scheduleAFixedDelayJob(instance, methodName, inputTypes, inputParams, initialDelay, taskDelay, timeUnit); } /** * To avoid data serialization issues for jobdata that is persisted in the database inpurParams should be of * primitive or String type */ @Override public String scheduleAConfigurableDelayJob(Object instance, String methodName, Class<?>[] inputTypes, Object[] inputParams, long initialDelay, String configurableDelayKeyName, TimeUnit timeUnit) { if (!validate(instance, inputTypes)) { return null; } return super.scheduleAConfigurableDelayJob(instance, methodName, inputTypes, inputParams, initialDelay, configurableDelayKeyName, timeUnit); } /** * To avoid data serialization issues for jobdata that is persisted in the database inpurParams should be of * primitive or String type */ @Override public String scheduleAOneTimeJob(Object instance, String methodName, Class<?>[] inputTypes, Object[] inputParams, long initialDelay, TimeUnit timeUnit) { if (!validate(instance, inputTypes)) { return null; } return super.scheduleAOneTimeJob(instance, methodName, inputTypes, inputParams, initialDelay, timeUnit); } /** * To avoid data serialization issues for jobdata that is persisted in the database inpurParams should be of * primitive or String type */ @Override public String scheduleACronJob(Object instance, String methodName, Class<?>[] inputTypes, Object[] inputParams, String cronExpression) { if (!validate(instance, inputTypes)) { return null; } return super.scheduleACronJob(instance, methodName, inputTypes, inputParams, cronExpression); } @Override public String scheduleACronJob(Object instance, String methodName, Class<?>[] inputTypes, Object[] inputParams, String cronExpression, Date startAt, Date endBy) { if (!validate(instance, inputTypes)) { throw new RuntimeException("Failed to validate input parameters for scheduling. Only primitives or String values are allowed."); } return super.scheduleACronJob(instance, methodName, inputTypes, inputParams, cronExpression, startAt, endBy); } @Override protected JobDetail createJobWithBasicMapValues(Object instance, String methodName, Class<?>[] inputTypes, Object[] inputParams) { String jobName = generateUniqueNameForInstance(instance, methodName); boolean allowsConcurrent = JobWrapper.methodAllowsConcurrent(instance, methodName); Class<? extends Job> jobType = allowsConcurrent ? PersistentJobWrapper.class : PersistentSequentialJobWrapper.class; JobDetail job = newJob() .withIdentity(jobName, Scheduler.DEFAULT_GROUP) .ofType(jobType) .build(); setBasicMapValues(job.getJobDataMap(), instance, methodName, inputTypes, inputParams); return job; } private boolean validate(Object instance, Class<?>[] inputTypes) { boolean validation = true; for (Class<?> cls : inputTypes) { if (!(cls.isPrimitive() || ClassUtils.wrapperToPrimitive(cls) != null || cls.isAssignableFrom(String.class))) { validation = false; log.error("Only primitives or String parameter types are supported for persistent jobs. '{}' is not supported", cls.getSimpleName()); } } return validation; } /* * The JobData for persistent jobs should contain only primitives We do NOT store the instance of the class passed, * only the name. */ private void setBasicMapValues(JobDataMap data, Object instance, String methodName, Class<?>[] inputTypes, Object[] inputParams) { data.put(RUNNABLE_INSTANCE, instance.getClass().getName()); data.put(RUN_METHOD_NAME, methodName); data.put(RUN_METHOD_PARAM, inputParams); data.put(RUN_METHOD_PARAM_TYPE, inputTypes); } }