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 // the name under which this app serves XML-RPC requests. Defaults to the app name
private String xmlrpcHandlerName; 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 * Build an application with the given name in the app directory. No Server-wide
* properties are created or used. * properties are created or used.
@ -305,6 +310,8 @@ public final class Application implements IPathElement, Runnable {
} }
activeRequests = new Hashtable(); activeRequests = new Hashtable();
activeCronJobs = new WeakHashMap();
customCronJobs = new Hashtable();
skinmgr = new SkinManager(this); skinmgr = new SkinManager(this);
@ -1355,42 +1362,88 @@ public final class Application implements IPathElement, Runnable {
} }
} }
// check if we should call scheduler if ((cronJobs == null) || (props.lastModified() > lastPropertyRead)) {
if ((now - lastScheduler) > scheduleSleep) { updateProperties();
lastScheduler = now; cronJobs = CronJob.parse(props);
Object val = null;
try {
val = eval.invokeFunction((INode) null, "scheduler", new Object[0]);
} catch (Exception ignore) {
}
try {
int ret = ((Number) val).intValue();
if (ret < 1000) {
scheduleSleep = 60000L;
} else {
scheduleSleep = ret;
}
} catch (Exception ignore) {
}
// logEvent ("Called scheduler for "+name+", will sleep for "+scheduleSleep+" millis");
} }
// sleep until we have work to do Date d = new Date();
List jobs = (List) cronJobs.clone();
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 {
break;
}
}
// 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);
r.start();
activeCronJobs.put(j.getName(), r);
} else {
try {
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 { try {
worker.sleep(Math.min(cleanupSleep, scheduleSleep)); worker.sleep(CronJob.millisToNextFullMinute());
} catch (InterruptedException x) { } catch (InterruptedException x) {
logEvent("Scheduler for " + name + " interrupted"); logEvent("Scheduler for " + name + " interrupted");
worker = null; worker = null;
break; 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"); logEvent("Scheduler for " + name + " exiting");
} }
@ -1761,4 +1814,29 @@ public final class Application implements IPathElement, Runnable {
logEvent("error loading session data: " + e.toString()); 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; package helma.framework.core;
import helma.objectmodel.INode; import helma.objectmodel.INode;
import helma.util.CronJob;
import java.io.File; import java.io.File;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
@ -254,6 +255,45 @@ public class ApplicationBean implements Serializable {
return (SessionBean[]) userSessions.toArray(new SessionBean[0]); 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 // getter methods for readonly properties of this application
public int getcacheusage() { public int getcacheusage() {
return app.getCacheUsage(); return app.getCacheUsage();

View file

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

View file

@ -86,7 +86,7 @@ public class CronJob {
private String name = null; private String name = null;
private String function = null; private String function = null;
private long timeout = 30000; private long timeout = 20000;
/** A method for parsing properties. It looks through the properties /** A method for parsing properties. It looks through the properties
* file for entries that look like this: * 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 (); Hashtable jobs = new Hashtable ();
Enumeration e = props.keys (); Enumeration e = props.keys ();
while (e.hasMoreElements ()) { while (e.hasMoreElements ()) {
@ -195,13 +214,40 @@ public class CronJob {
parseHour (job, value); parseHour (job, value);
} else if (jobSpec.equalsIgnoreCase("minute")) { } else if (jobSpec.equalsIgnoreCase("minute")) {
parseMinute (job, value); parseMinute (job, value);
} else if (jobSpec.equalsIgnoreCase("timeout")) {
parseTimeout (job, value);
} }
} catch (NoSuchElementException nsee) { } 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;
}
public static void parseYear (CronJob job, String value) { public static void parseYear (CronJob job, String value) {
if (value.equals("*")) { if (value.equals("*")) {
@ -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. * Create an empty CronJob.
@ -657,13 +719,6 @@ public class CronJob {
this.timeout = timeout; this.timeout = timeout;
} }
/**
* Set this entry's timeout
*/
public void setTimeout(String timeout)
{
this.timeout = Long.valueOf(timeout).longValue ();
}
/** /**
* Get this entry's timeout * Get this entry's timeout
@ -672,4 +727,9 @@ public class CronJob {
{ {
return this.timeout; return this.timeout;
} }
public String toString () {
return "[CronJob " + name + "]";
}
} }