merged changes from helma_1_2_cron branch (replacing the scheduler with

a cron-like setup)
This commit is contained in:
stefanp 2003-04-11 13:00:23 +00:00
parent c8674a5f3d
commit b522a740df
4 changed files with 234 additions and 38 deletions

View file

@ -146,6 +146,11 @@ public final class Application implements IPathElement, Runnable {
// the name under which this app serves XML-RPC requests. Defaults to the app name
private String xmlrpcHandlerName;
// the list of cron jobs
private Map activeCronJobs = null;
private Vector cronJobs = null;
Hashtable customCronJobs = null;
/**
* Build an application with the given name in the app directory. No Server-wide
* properties are created or used.
@ -305,6 +310,8 @@ public final class Application implements IPathElement, Runnable {
}
activeRequests = new Hashtable();
activeCronJobs = new WeakHashMap();
customCronJobs = new Hashtable();
skinmgr = new SkinManager(this);
@ -1355,42 +1362,88 @@ public final class Application implements IPathElement, Runnable {
}
}
// check if we should call scheduler
if ((now - lastScheduler) > scheduleSleep) {
lastScheduler = now;
Object val = null;
try {
val = eval.invokeFunction((INode) null, "scheduler", new Object[0]);
} catch (Exception ignore) {
if ((cronJobs == null) || (props.lastModified() > lastPropertyRead)) {
updateProperties();
cronJobs = CronJob.parse(props);
}
try {
int ret = ((Number) val).intValue();
Date d = new Date();
List jobs = (List) cronJobs.clone();
if (ret < 1000) {
scheduleSleep = 60000L;
jobs.addAll(customCronJobs.values());
CronJob.sort(jobs);
for (Iterator i = jobs.iterator(); i.hasNext();) {
CronJob j = (CronJob) i.next();
if (j.appliesToDate(d)) {
// check if the job is already active ...
if (activeCronJobs.containsKey(j.getName())) {
logEvent(j + " is still active, skipped in this minute");
continue;
}
RequestEvaluator thisEvaluator;
try {
thisEvaluator = getEvaluator();
} catch (RuntimeException rt) {
if (stopped == false) {
logEvent("couldn't execute " + j +
", maximum thread count reached");
continue;
} else {
scheduleSleep = ret;
break;
}
} catch (Exception ignore) {
}
// logEvent ("Called scheduler for "+name+", will sleep for "+scheduleSleep+" millis");
}
// if the job has a long timeout or we're already late during this minute
// the job is run from an extra thread
if ((j.getTimeout() > 20000) ||
(CronJob.millisToNextFullMinute() < 30000)) {
CronRunner r = new CronRunner(thisEvaluator, j);
// sleep until we have work to do
r.start();
activeCronJobs.put(j.getName(), r);
} else {
try {
worker.sleep(Math.min(cleanupSleep, scheduleSleep));
thisEvaluator.invokeFunction((INode) null, j.getFunction(),
new Object[0], j.getTimeout());
} catch (Exception ex) {
logEvent("error running " + j + ": " + ex.toString());
} finally {
if (stopped == false) {
releaseEvaluator(thisEvaluator);
}
}
}
thisEvaluator = null;
}
}
// sleep until the next full minute
try {
worker.sleep(CronJob.millisToNextFullMinute());
} catch (InterruptedException x) {
logEvent("Scheduler for " + name + " interrupted");
worker = null;
break;
}
}
// when interrupted, shutdown running cron jobs
synchronized (activeCronJobs) {
for (Iterator i = activeCronJobs.keySet().iterator(); i.hasNext();) {
String jobname = (String) i.next();
((CronRunner) activeCronJobs.get(jobname)).interrupt();
activeCronJobs.remove(jobname);
}
}
logEvent("Scheduler for " + name + " exiting");
}
@ -1761,4 +1814,29 @@ public final class Application implements IPathElement, Runnable {
logEvent("error loading session data: " + e.toString());
}
}
class CronRunner extends Thread {
RequestEvaluator thisEvaluator;
CronJob job;
public CronRunner(RequestEvaluator thisEvaluator, CronJob job) {
this.thisEvaluator = thisEvaluator;
this.job = job;
}
public void run() {
try {
thisEvaluator.invokeFunction((INode) null, job.getFunction(),
new Object[0], job.getTimeout());
} catch (Exception ex) {
}
if (stopped == false) {
releaseEvaluator(thisEvaluator);
}
thisEvaluator = null;
activeCronJobs.remove(job.getName());
}
}
}

View file

@ -17,6 +17,7 @@
package helma.framework.core;
import helma.objectmodel.INode;
import helma.util.CronJob;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
@ -254,6 +255,45 @@ public class ApplicationBean implements Serializable {
return (SessionBean[]) userSessions.toArray(new SessionBean[0]);
}
/**
*
*
* @param functionName ...
*/
public void addCronJob(String functionName) {
CronJob job = new CronJob(functionName);
job.setFunction(functionName);
app.customCronJobs.put(functionName, job);
}
/**
*
*
* @param functionName ...
* @param year ...
* @param month ...
* @param day ...
* @param weekday ...
* @param hour ...
* @param minute ...
*/
public void addCronJob(String functionName, String year, String month, String day,
String weekday, String hour, String minute) {
CronJob job = CronJob.newJob(functionName, year, month, day, weekday, hour, minute);
app.customCronJobs.put(functionName, job);
}
/**
*
*
* @param functionName ...
*/
public void removeCronJob(String functionName) {
app.customCronJobs.remove(functionName);
}
// getter methods for readonly properties of this application
public int getcacheusage() {
return app.getCacheUsage();

View file

@ -737,6 +737,25 @@ public final class RequestEvaluator implements Runnable {
public synchronized Object invokeFunction(Object object, String functionName,
Object[] args)
throws Exception {
// give internal call more time (15 minutes) to complete
return invokeFunction(object, functionName, args, 60000L * 15);
}
/**
*
*
* @param object ...
* @param functionName ...
* @param args ...
* @param timeout ...
*
* @return ...
*
* @throws Exception ...
*/
public synchronized Object invokeFunction(Object object, String functionName,
Object[] args, long timeout)
throws Exception {
reqtype = INTERNAL;
session = null;
thisObject = object;
@ -747,7 +766,7 @@ public final class RequestEvaluator implements Runnable {
exception = null;
checkThread();
wait(60000L * 15); // give internal call more time (15 minutes) to complete
wait(timeout);
if (reqtype != NONE) {
stopThread();
@ -799,7 +818,6 @@ public final class RequestEvaluator implements Runnable {
if (exception != null) {
throw (exception);
}
return result;
}

View file

@ -86,7 +86,7 @@ public class CronJob {
private String name = null;
private String function = null;
private long timeout = 30000;
private long timeout = 20000;
/** A method for parsing properties. It looks through the properties
* file for entries that look like this:
@ -161,7 +161,26 @@ public class CronJob {
*/
public static Collection parse(Properties props) {
public static CronJob newJob (String functionName, String year, String month, String day, String weekday, String hour, String minute) {
CronJob job = new CronJob (functionName);
job.setFunction (functionName);
if (year != null)
parseYear (job, year);
if (month != null)
parseMonth (job, month);
if (day != null)
parseDay (job, day);
if (weekday != null)
parseWeekDay (job, weekday);
if (hour != null)
parseHour (job, hour);
if (minute != null)
parseMinute (job, minute);
return job;
}
public static Vector parse(Properties props) {
Hashtable jobs = new Hashtable ();
Enumeration e = props.keys ();
while (e.hasMoreElements ()) {
@ -195,11 +214,38 @@ public class CronJob {
parseHour (job, value);
} else if (jobSpec.equalsIgnoreCase("minute")) {
parseMinute (job, value);
} else if (jobSpec.equalsIgnoreCase("timeout")) {
parseTimeout (job, value);
}
} catch (NoSuchElementException nsee) {
}
}
return jobs.values ();
Vector jobVec = new Vector (jobs.values ());
return (Vector) sort (jobVec);
}
public static List sort (List list) {
Collections.sort (list, new Comparator() {
public int compare (Object o1, Object o2) {
CronJob cron1 = (CronJob) o1;
CronJob cron2 = (CronJob) o2;
if (cron1.getTimeout () > cron2.getTimeout ())
return 1;
else if (cron1.getTimeout () < cron2.getTimeout ())
return -1;
else
return 0;
}
public boolean equals (Object o1, Object o2) {
if (o1!=null) {
return o1.equals (o2);
} else {
return false;
}
}
});
return list;
}
@ -351,6 +397,22 @@ public class CronJob {
}
public static void parseTimeout (CronJob job, String timeout) {
long timeoutValue = 1000 * Long.valueOf(timeout).longValue ();
job.setTimeout (timeoutValue);
}
public static long nextFullMinute () {
long now = System.currentTimeMillis();
long millisAfterMinute = (now % 60000);
return (now + 60000 - millisAfterMinute);
}
public static long millisToNextFullMinute () {
long now = System.currentTimeMillis();
long millisAfterMinute = (now % 60000);
return (60000 - millisAfterMinute);
}
/**
* Create an empty CronJob.
@ -657,13 +719,6 @@ public class CronJob {
this.timeout = timeout;
}
/**
* Set this entry's timeout
*/
public void setTimeout(String timeout)
{
this.timeout = Long.valueOf(timeout).longValue ();
}
/**
* Get this entry's timeout
@ -672,4 +727,9 @@ public class CronJob {
{
return this.timeout;
}
public String toString () {
return "[CronJob " + name + "]";
}
}