/*
* $Id$
*
* Copyright 2008 Glencoe Software, Inc. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package ome.services.scheduler;
import java.util.HashMap;
import java.util.Map;
import ome.tools.spring.OnContextRefreshedEventListener;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.SchedulingException;
import org.springframework.scheduling.quartz.JobDetailAwareTrigger;
/**
* Produces a <a href="http://www.opensymphony.com/quartz/Quartz</a>
* {@link Scheduler} which automatically loads all the triggers it can find.
*
* @author Josh Moore, josh at glencoesoftware.com
* @since 3.0-Beta3
*/
public class SchedulerFactoryBean extends
org.springframework.scheduling.quartz.SchedulerFactoryBean implements
ApplicationListener<ContextRefreshedEvent>,
ApplicationContextAware {
private final static Logger log = LoggerFactory
.getLogger(SchedulerFactoryBean.class);
private final Map<String, Trigger> triggers = new HashMap<String, Trigger>();
/**
* Already subclassing another class, so re-using the handler here
* in a somewhat awkward to re-use the code.
*/
private final OnContextRefreshedEventListener handler =
new OnContextRefreshedEventListener(true, Integer.MAX_VALUE) {
@Override
public void handleContextRefreshedEvent(ContextRefreshedEvent event) {
handle(event);
}
};
public void setApplicationContext(ApplicationContext ctx) {
handler.setApplicationContext(ctx);
}
public void onApplicationEvent(ContextRefreshedEvent event) {
handler.onApplicationEvent(event);
}
private void handle(ContextRefreshedEvent cre) {
String[] names = cre.getApplicationContext().getBeanNamesForType(
Trigger.class);
for (String name : names) {
if (triggers.containsKey(name)) {
log.error("Scheduler already has trigger named: " + name);
continue;
}
Trigger trigger = (Trigger) cre.getApplicationContext()
.getBean(name);
registerTrigger(name, trigger);
}
restartIfNeeded();
}
/**
* Registers a {@link JobDetailAwareTrigger}. A method like this should
* really have protected visibility in the superclass.
*/
protected void registerTrigger(String beanName, Trigger trigger) {
try {
Scheduler scheduler = (Scheduler) getObject();
triggers.put(beanName, trigger);
JobDetailAwareTrigger jdat = (JobDetailAwareTrigger) trigger;
JobDetail job = jdat.getJobDetail();
scheduler.addJob(job, false);
scheduler.scheduleJob(trigger);
log.debug(String.format("Registered trigger \"%s\": %s", beanName, trigger));
} catch (SchedulerException se) {
throw new RuntimeException(se);
}
}
/**
* Similar to the {@link #isRunning()} method, but properly handles the
* situation where the {@link Scheduler} has been completely shutdown and
* therefore must be replaced.
*/
protected void restartIfNeeded() {
if (!isRunning()) {
try {
start();
} catch (SchedulingException se) {
log.info("Replacing scheduler");
try {
afterPropertiesSet();
if (!isRunning()) {
start();
}
} catch (Exception e) {
throw new RuntimeException("Failed to restart scheduler", e);
}
}
}
}
}