/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY 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 along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.scheduler.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.calendar.BusinessCalendar;
import org.jbpm.calendar.Duration;
import org.jbpm.db.SchedulerSession;
import org.jbpm.scheduler.exe.Timer;
public class SchedulerThread extends Thread {
static BusinessCalendar businessCalendar = new BusinessCalendar();
JbpmConfiguration jbpmConfiguration = null;
String jbpmContextName = null;
List listeners = new ArrayList();
boolean keepRunning = true;
long interval = 5000;
public SchedulerThread() {
this(JbpmConfiguration.getInstance(), JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
}
public SchedulerThread(JbpmConfiguration jbpmConfiguration) {
this(jbpmConfiguration, JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
}
public SchedulerThread(String jbpmContextName) {
this(JbpmConfiguration.getInstance(), jbpmContextName);
}
public SchedulerThread(JbpmConfiguration jbpmConfiguration, String jbpmContextName) {
super("JbpmScheduler");
this.jbpmConfiguration = jbpmConfiguration;
this.jbpmContextName = jbpmContextName;
}
public void run() {
while (keepRunning) {
long millisToWait = interval;
try {
millisToWait = executeTimers();
// calculate the milliseconds to wait...
if (millisToWait < 0) {
millisToWait = interval;
}
millisToWait = Math.min(millisToWait, interval);
} catch (RuntimeException e) {
log.info("runtime exception while executing timers", e);
} finally {
try {
Thread.sleep(millisToWait);
} catch (InterruptedException e) {
log.info("waiting for timers got interuppted");
}
}
}
log.info("ending scheduler thread");
}
/**
* executes due timers and calculates the time before the next timer is due.
* @return the number of milliseconds till the next job is due or -1 if no timer is
* schedulerd in the future.
*/
public long executeTimers() {
long millisTillNextTimerIsDue = -1;
boolean isDueDateInPast = true;
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext(jbpmContextName);
try {
SchedulerSession schedulerSession = jbpmContext.getSchedulerSession();
log.debug("checking for timers");
Iterator iter = schedulerSession.findTimersByDueDate();
while( (iter.hasNext())
&& (isDueDateInPast)
) {
Timer timer = (Timer) iter.next();
log.debug("found timer "+timer);
// if this timer is due
if (timer.isDue()) {
log.debug("executing timer '"+timer+"'");
// execute
timer.execute();
// save the process instance
jbpmContext.save(timer.getProcessInstance());
// notify the listeners (e.g. the scheduler servlet)
notifyListeners(timer);
// if there was an exception, just save the timer
if (timer.getException()!=null) {
schedulerSession.saveTimer(timer);
// if repeat is specified
} else if (timer.getRepeat()!=null) {
// update timer by adding the repeat duration
Date dueDate = timer.getDueDate();
// suppose that it took the timer runner thread a
// very long time to execute the timers.
// then the repeat action dueDate could already have passed.
while (dueDate.getTime()<=System.currentTimeMillis()) {
dueDate = businessCalendar
.add(dueDate,
new Duration(timer.getRepeat()));
}
timer.setDueDate( dueDate );
// save the updated timer in the database
log.debug("saving updated timer for repetition '"+timer+"' in '"+(dueDate.getTime()-System.currentTimeMillis())+"' millis");
schedulerSession.saveTimer(timer);
} else {
// delete this timer
log.debug("deleting timer '"+timer+"'");
schedulerSession.deleteTimer(timer);
}
} else { // this is the first timer that is not yet due
isDueDateInPast = false;
millisTillNextTimerIsDue = timer.getDueDate().getTime() - System.currentTimeMillis();
}
}
} finally {
jbpmContext.close();
}
return millisTillNextTimerIsDue;
}
// listeners ////////////////////////////////////////////////////////////////
public void addListener(SchedulerListener listener) {
if (listeners==null) listeners = new ArrayList();
listeners.add(listener);
}
public void removeListener(SchedulerListener listener) {
listeners.remove(listener);
if (listeners.isEmpty()) {
listeners = null;
}
}
void notifyListeners(Timer timer) {
if (listeners!=null) {
Date now = new Date();
Iterator iter = new ArrayList(listeners).iterator();
while (iter.hasNext()) {
SchedulerListener timerRunnerListener = (SchedulerListener) iter.next();
timerRunnerListener.timerExecuted(now, timer);
}
}
}
public void setInterval(long interval) {
this.interval = interval;
}
private static final Log log = LogFactory.getLog(SchedulerThread.class);
}