package pl.net.bluesoft.rnd.pt.ext.sched.impl;
import org.hibernate.Session;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.calendar.HolidayCalendar;
import org.quartz.impl.matchers.EverythingMatcher;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.utils.Key;
import pl.net.bluesoft.rnd.processtool.ProcessToolContext;
import pl.net.bluesoft.rnd.processtool.ReturningProcessToolContextCallback;
import pl.net.bluesoft.rnd.processtool.plugins.ProcessToolRegistry;
import pl.net.bluesoft.rnd.pt.ext.sched.event.ScheduleJobEvent;
import pl.net.bluesoft.rnd.pt.ext.sched.model.SchedulerProperty;
import pl.net.bluesoft.rnd.pt.ext.sched.service.ProcessToolSchedulerService;
import pl.net.bluesoft.rnd.pt.ext.sched.service.SchedulerServiceInternalError;
import pl.net.bluesoft.util.eventbus.EventListener;
import pl.net.bluesoft.util.lang.Formats;
import pl.net.bluesoft.util.lang.Predicate;
import pl.net.bluesoft.util.lang.Strings;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author: amichalak@bluesoft.net.pl
*/
public class QuartzSchedulerService implements ProcessToolSchedulerService, EventListener<ScheduleJobEvent> {
private static final Logger logger = Logger.getLogger(QuartzSchedulerService.class.getName());
private ProcessToolRegistry registry;
private Scheduler scheduler;
private Properties schedulerProperties;
private JobExecutionListener jobListener;
public QuartzSchedulerService(ProcessToolRegistry registry, Properties fileSchedulerProperties) {
this.registry = registry;
this.schedulerProperties = mergeProperties(fileSchedulerProperties, loadPersistentProperties());
this.jobListener = new JobExecutionListener(registry);
}
public void init() throws SchedulerException {
logger.warning("Starting scheduler service...");
scheduler = new StdSchedulerFactory(schedulerProperties).getScheduler();
scheduler.addCalendar(QuartzSchedulerService.class.getName(), new HolidayCalendar(), true, true);
scheduler.getListenerManager().addJobListener(jobListener, EverythingMatcher.allJobs());
scheduler.start();
logger.warning("Scheduler service online!");
}
public void destroy() throws SchedulerException {
logger.warning("Stopping scheduler service...");
scheduler.clear();
scheduler.shutdown();
logger.warning("Scheduler service offline!");
}
private Properties mergeProperties(Properties fileSchedulerProperties, Properties persistentProperties) {
Properties mergedProperties = new Properties(persistentProperties);
for (String key : fileSchedulerProperties.stringPropertyNames()) {
String value = mergedProperties.getProperty(key);
if (value == null) {
mergedProperties.put(key, fileSchedulerProperties.getProperty(key));
}
}
return mergedProperties;
}
private Properties loadPersistentProperties() {
return registry.withProcessToolContext(new ReturningProcessToolContextCallback<Properties>() {
@Override
public Properties processWithContext(ProcessToolContext ctx) {
Properties properties = new Properties();
Session session = ctx.getHibernateSession();
List<SchedulerProperty> list = session.createCriteria(SchedulerProperty.class).list();
for (SchedulerProperty prop : list) {
properties.put(prop.getName(), prop.getValue());
}
return properties;
}
});
}
@Override
public void enableJobQuietMode(final Class<? extends Job>... jobClasses) {
logger.info("Enabled quiet mode for jobs: " + Formats.joinClassNames(jobClasses));
jobListener.addSilentJobs(jobClasses);
}
@Override
public void disableJobQuietMode(Class<? extends Job>... jobClasses) {
logger.info("Disabled quiet mode for jobs: " + Formats.joinClassNames(jobClasses));
jobListener.removeSilentJobs(jobClasses);
}
@Override
public void scheduleJob(final JobDetail jobDetail, final Trigger... triggers) {
final StringBuilder sb = new StringBuilder();
final List<Trigger> list = new ArrayList<Trigger>() {{
for (int i = 0; i < triggers.length; ++i) {
Trigger t = triggers[i];
add(t);
sb.append("[").append(t.getStartTime()).append("]");
if (i != triggers.length - 1) {
sb.append(", ");
}
}
}};
Map<JobDetail, List<Trigger>> jobMap = new HashMap<JobDetail, List<Trigger>>() {{
put(jobDetail, list);
}};
logger.info("Scheduling job on: " + sb + " of type: " + jobDetail.getJobClass().getName());
try {
scheduler.scheduleJobs(jobMap, true);
}
catch (SchedulerException e) {
logger.log(Level.SEVERE, "Error while scheduling job: " + e.getMessage(), e);
}
}
@Override
public void cancelScheduledJobs(String jobGroupName, final Class<? extends Job>... jobClasses) {
cancelJobGroupInternal(jobGroupName, new Predicate<JobDetail>() {
Set<String> classNames = new HashSet<String>(jobClasses.length) {{
for (Class<? extends Job> clazz : jobClasses) {
add(clazz.getName());
}
}};
@Override
public boolean apply(JobDetail jobDetail) {
return classNames.contains(jobDetail.getJobClass().getName());
}
});
}
@Override
public void cancelScheduledJobGroup(String jobGroupName) {
cancelJobGroupInternal(jobGroupName, new Predicate<JobDetail>() {
@Override
public boolean apply(JobDetail jobDetail) {
return true;
}
});
}
private void cancelJobGroupInternal(String jobGroupName, Predicate<JobDetail> predicate) {
try {
if (!Strings.hasLength(jobGroupName)) {
jobGroupName = Key.DEFAULT_GROUP;
}
List<TriggerKey> cancelledTriggerKeys = new ArrayList<TriggerKey>();
Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(jobGroupName));
for (JobKey jobKey : jobKeys) {
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
if (predicate.apply(jobDetail)) {
List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
for (Trigger trigger : triggers) {
cancelledTriggerKeys.add(trigger.getKey());
}
}
}
logger.warning("Cancelling " + cancelledTriggerKeys.size() + " jobs for job group: " + jobGroupName);
scheduler.unscheduleJobs(cancelledTriggerKeys);
}
catch (SchedulerException e) {
throw new SchedulerServiceInternalError(e);
}
}
@Override
public void onEvent(ScheduleJobEvent scheduleJobEvent) {
scheduleJob(scheduleJobEvent.getJobDetail(), scheduleJobEvent.getTrigger());
}
}