/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006, JBoss Inc.
*/
package org.jboss.soa.esb.schedule;
import org.apache.log4j.Logger;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.util.ClassUtil;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* Schedule Provider.
* <p/>
* Manages all of the configured scheduled jobs and their listeners.
*
* @author <a href="daniel.bevenius@redpill.se">Daniel Bevenius</a>
* @author <a href="mailto:tom.fennelly@jboss.com">tom.fennelly@jboss.com</a>
* @deprecated {@link ScheduleResource}
*/
public class ScheduleProvider {
private static Logger logger = Logger.getLogger(ScheduleProvider.class);
/* Quartz scheduler instance */
private Scheduler scheduler;
private Map<JobDetail, Trigger> scheduledJobs = new HashMap<JobDetail, Trigger>();
private List<Schedule> schedules;
private boolean active ;
private final ClassLoader tcc ;
private static final String JBOSS_ESB = "JBossESB";
public ScheduleProvider(Properties properties, List<Schedule> schedules) throws ConfigurationException {
try {
Properties defaultProperties = new Properties();
InputStream quartzProperties = ClassUtil.getResourceAsStream("quartz.properties", ScheduleProvider.class);
if(quartzProperties != null) {
defaultProperties.load(quartzProperties);
}
if(properties != null && !properties.isEmpty()) {
// Overlay the defaults with the configs on the schedule config...
defaultProperties.putAll(properties);
}
if(!defaultProperties.isEmpty()) {
scheduler = new StdSchedulerFactory(defaultProperties).getScheduler();
} else {
scheduler = new StdSchedulerFactory().getScheduler();
}
scheduler.start() ;
tcc = Thread.currentThread().getContextClassLoader() ;
} catch (SchedulerException e) {
throw new ConfigurationException("Unable to create Scheduler instance.", e);
} catch (IOException e) {
throw new ConfigurationException("Unable to create Scheduler instance. Failed to read", e);
}
this.schedules = schedules;
}
public void addListener(ScheduledEventListener listener, String scheduleId) throws SchedulingException, ConfigurationException {
Schedule schedule = getSchedule(scheduleId);
Trigger trigger;
if(schedule == null) {
throw new ConfigurationException("Unknown schedule '" + scheduleId + "' referenced in listener configuration.");
}
if(schedule instanceof SimpleSchedule) {
SimpleSchedule simpleSchedule = (SimpleSchedule) schedule;
if(simpleSchedule.getExecCount() == -1) {
trigger = new SimpleTrigger();
((SimpleTrigger)trigger).setRepeatInterval(simpleSchedule.getFrequency());
((SimpleTrigger)trigger).setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
} else if(simpleSchedule.getExecCount() == 0) {
logger.warn("<simple-schedule> '" + scheduleId + "' has an execCount of 0 configured. This schedule will not fire!");
return;
} else {
// So the repeatCount = execCount - 1 ....
trigger = new SimpleTrigger();
((SimpleTrigger)trigger).setRepeatInterval(simpleSchedule.getFrequency());
((SimpleTrigger)trigger).setRepeatCount(simpleSchedule.getExecCount() - 1);
}
trigger.setName(simpleSchedule.getScheduleid());
} else {
String cronExpression = ((CronSchedule)schedule).getCronExpression();
try {
trigger = new CronTrigger(scheduleId, JBOSS_ESB, cronExpression);
} catch (ParseException e) {
throw new ConfigurationException("Invalid CRON expression '" + cronExpression + "' on schedule '" + scheduleId + "'.", e);
}
}
// Set the start and end times, if configured....
if(schedule.getStartDate() != null) {
trigger.setStartTime(schedule.getStartDate().getTime());
} else {
trigger.setStartTime(new Date());
}
if(schedule.getEndDate() != null) {
trigger.setEndTime(schedule.getEndDate().getTime());
}
addListener(listener, trigger);
}
public void addListener(ScheduledEventListener listener, long scheduleFrequency) throws SchedulingException {
Trigger trigger = TriggerUtils.makeSecondlyTrigger((int) scheduleFrequency);
String name = (listener.toString() + scheduleFrequency);
trigger.setName(name);
trigger.setStartTime(new Date());
addListener(listener, trigger);
}
private static volatile int nameDelta = 1;
private void addListener(ScheduledEventListener listener, Trigger trigger) throws SchedulingException {
final int id ;
synchronized (ScheduleProvider.class) {
id = nameDelta++ ;
}
JobDataMap jobDataMap = new JobDataMap();
JobDetail jobDetail;
String name = trigger.getName();
name += "-" + id;
trigger.setName(name);
jobDetail = new JobDetail(name, JBOSS_ESB, ScheduleProvider.ESBScheduledJob.class);
jobDataMap.put(ScheduledEventListener.class.getName(), listener);
jobDataMap.put(ClassLoader.class.getName(), tcc);
jobDetail.setJobDataMap(jobDataMap);
synchronized(this)
{
scheduledJobs.put(jobDetail, trigger);
if (active)
{
try {
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
throw new SchedulingException("Failed to schedule job.", e);
}
}
}
}
private Schedule getSchedule(String scheduleId) {
for(Schedule schedule : schedules) {
if(schedule.getScheduleid().equals(scheduleId.trim())) {
return schedule;
}
}
return null;
}
/**
* Start the scheduler.
* @throws SchedulingException Start failed.
*/
public synchronized void start() throws SchedulingException {
if (!active) {
try {
for (Map.Entry<JobDetail, Trigger> entry: scheduledJobs.entrySet()) {
scheduler.scheduleJob(entry.getKey(), entry.getValue());
}
} catch (SchedulerException e) {
throw new SchedulingException("Failed to start scheduling.", e);
}
active = true ;
}
}
/**
* Stop the scheduler.
* @throws SchedulingException Stop failed.
*/
public synchronized void stop() throws SchedulingException {
if (active) {
try {
for(Trigger trigger: scheduledJobs.values()) {
final String jobName = trigger.getName() ;
if(!scheduler.deleteJob(jobName, JBOSS_ESB)) {
logger.info("Failed to delete scheduled Job '" + jobName + "' from job group '" + JBOSS_ESB + "'. Job run may have already completed.");
}
}
} catch (SchedulerException e) {
throw new SchedulingException("Failed to shutdown scheduling.", e);
}
active = false ;
}
}
public static class ESBScheduledJob implements StatefulJob {
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
final JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap() ;
ScheduledEventListener listener = (ScheduledEventListener) jobDataMap.get(ScheduledEventListener.class.getName());
final ClassLoader tcc = (ClassLoader)jobDataMap.get(ClassLoader.class.getName());
final Thread thread = Thread.currentThread() ;
final ClassLoader currentClassLoader = thread.getContextClassLoader() ;
thread.setContextClassLoader(tcc) ;
try {
listener.onSchedule();
} catch (SchedulingException e) {
JobExecutionException jobException = new JobExecutionException("Scheduling exception on " + jobExecutionContext.getJobDetail().getName());
jobException.initCause(e);
throw jobException;
} catch (final Throwable th) {
JobExecutionException jobException = new JobExecutionException("Unexpected exception on " + jobExecutionContext.getJobDetail().getName());
jobException.initCause(th);
throw jobException;
} finally {
thread.setContextClassLoader(currentClassLoader) ;
}
}
}
}