merged changes from helma_1_2_cron branch (replacing the scheduler with
a cron-like setup)
This commit is contained in:
parent
c8674a5f3d
commit
b522a740df
4 changed files with 234 additions and 38 deletions
|
@ -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 {
|
Date d = new Date();
|
||||||
int ret = ((Number) val).intValue();
|
List jobs = (List) cronJobs.clone();
|
||||||
|
|
||||||
if (ret < 1000) {
|
jobs.addAll(customCronJobs.values());
|
||||||
scheduleSleep = 60000L;
|
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 {
|
} 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 {
|
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) {
|
} 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,11 +214,38 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue