/*
*
* Copyright (c) 1997-1999 Scott Oaks and Henry Wong. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and
* without fee is hereby granted.
*
* This sample source code is provided for example only,
* on an unsupported, as-is basis.
*
* AUTHOR MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. AUTHOR SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). AUTHOR
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
* HIGH RISK ACTIVITIES.
*/
import java.util.*;
public class JobScheduler implements Runnable {
final public static int ONCE = 1;
final public static int FOREVER = -1;
final public static long HOURLY = (long)60*60*1000;
final public static long DAILY = 24*HOURLY;
final public static long WEEKLY = 7*DAILY;
final public static long MONTHLY = -1;
final public static long YEARLY = -2;
private class JobNode {
public Runnable job;
public Date executeAt;
public long interval;
public int count;
}
private ThreadPool tp;
private DaemonLock dlock = new DaemonLock();
private Vector jobs = new Vector(100);
public JobScheduler(int poolSize) {
tp = (poolSize > 0) ? new ThreadPool(poolSize) : null;
Thread js = new Thread(this);
js.setDaemon(true);
js.start();
}
private synchronized void addJob(JobNode job) {
dlock.acquire();
jobs.addElement(job);
notify();
}
private synchronized void deleteJob(Runnable job) {
for (int i=0; i < jobs.size(); i++) {
if (((JobNode) jobs.elementAt(i)).job == job) {
System.out.println("deleting job");
jobs.removeElementAt(i);
dlock.release();
break;
}
}
}
private JobNode updateJobNode(JobNode jn) {
Calendar cal = Calendar.getInstance();
cal.setTime(jn.executeAt);
if (jn.interval == MONTHLY) {
// There is a minor bug. (see java.util.calendar)
cal.add(Calendar.MONTH, 1);
jn.executeAt = cal.getTime();
} else if (jn.interval == YEARLY) {
cal.add(Calendar.YEAR, 1);
jn.executeAt = cal.getTime();
} else {
jn.executeAt = new Date(jn.executeAt.getTime() + jn.interval);
}
jn.count = (jn.count == FOREVER) ? FOREVER : jn.count - 1;
return (jn.count != 0) ? jn : null;
}
private synchronized long runJobs() {
long minDiff = Long.MAX_VALUE;
long now = System.currentTimeMillis();
for (int i=0; i < jobs.size();) {
JobNode jn = (JobNode) jobs.elementAt(i);
if (jn.executeAt.getTime() <= now) {
System.out.println("Doing Job... " + i);
if (tp != null) {
tp.addRequest(jn.job);
} else {
Thread jt = new Thread(jn.job);
jt.setDaemon(false);
jt.start();
}
if (updateJobNode(jn) == null) {
System.out.println("Removing Job...");
jobs.removeElementAt(i);
dlock.release();
}
} else {
long diff = jn.executeAt.getTime() - now;
minDiff = Math.min(diff, minDiff);
i++;
}
}
return minDiff;
}
public synchronized void run() {
while (true) {
System.out.println("Checking Jobs...");
long waitTime = runJobs();
try {
System.out.println("Waiting for " + waitTime + " Milli-Seconds");
wait(waitTime);
} catch (Exception e) {};
}
}
public void execute(Runnable job) {
executeIn(job, (long)0);
}
public void executeIn(Runnable job, long millis) {
executeInAndRepeat(job, millis, 1000, ONCE);
}
public void executeInAndRepeat(Runnable job, long millis, long repeat) {
executeInAndRepeat(job, millis, repeat, FOREVER);
}
public void executeInAndRepeat(Runnable job, long millis, long repeat, int count) {
Date when = new Date(System.currentTimeMillis() + millis);
executeAtAndRepeat(job, when, repeat, count);
}
public void executeAt(Runnable job, Date when) {
executeAtAndRepeat(job, when, 1000, ONCE);
}
public void executeAtAndRepeat(Runnable job, Date when, long repeat) {
executeAtAndRepeat(job, when, repeat, FOREVER);
}
public void executeAtAndRepeat(Runnable job, Date when, long repeat, int count) {
JobNode jn = new JobNode();
jn.job = job;
jn.executeAt = when;
jn.interval = repeat;
jn.count = count;
addJob(jn);
}
public void cancel(Runnable job) {
deleteJob(job);
}
public void executeAtNextDOW(Runnable job, Date when, int DOW) {
Calendar target = Calendar.getInstance();
target.setTime(when);
while (target.get(Calendar.DAY_OF_WEEK) != DOW)
target.add(Calendar.DATE, 1);
System.out.println(target.getTime());
executeAt(job, target.getTime());
}
public void configureBackup(Runnable job) {
Calendar now = Calendar.getInstance();
System.out.println(now.getTime());
executeAtNextDOW(job, now.getTime(), Calendar.SUNDAY);
}
public static void main(String[] args)
throws Exception {
Runnable r1 = new Runnable() {
public void run() {
System.out.print("1");
try { Thread.sleep(5000); } catch (Exception ex) {};
System.out.print("1");
}
};
Runnable r2 = new Runnable() {
public void run() {
System.out.print("2");
try { Thread.sleep(5000); } catch (Exception ex) {};
System.out.print("2");
}
};
Runnable r3 = new Runnable() {
public void run() {
System.out.print("3");
}
};
Runnable r4 = new Runnable() {
public void run() {
System.out.print("4");
}
};
JobScheduler js = new JobScheduler(0);
Thread.sleep(1000);
// Test 1 - General Test
js.executeInAndRepeat(r1, 10000, 3000, 10);
js.executeInAndRepeat(r2, 20000, 1000, 10);
//Thread.sleep(11000);
//js.cancel(r1);
//js.cancel(r2);
//js.configureBackup(r1);
// Test 2 - Signature Test
//Date in10Sec = new Date(System.currentTimeMillis()+10000L);
//js.execute(r1);
//js.executeIn(r2, 2000L);
//js.executeInAndRepeat(r3, 10000L, 2000L);
//js.executeInAndRepeat(r4, 10000L, 2000L, 5);
//js.executeAt(r1, in10Sec);
//js.executeAtAndRepeat(r2, in10Sec, 2000L);
//js.executeAtAndRepeat(r3, in10Sec, 1000L, 5);
//js.cancel(r4);
//Thread.sleep(20000L);
//js.cancel(r2);
// Test 3 - Interval Test
//js.executeInAndRepeat(r3, 10000L, JobScheduler.HOURLY);
//js.executeInAndRepeat(r3, 10000L, JobScheduler.DAILY);
//js.executeInAndRepeat(r3, 10000L, JobScheduler.WEEKLY);
//js.executeInAndRepeat(r3, 10000L, JobScheduler.MONTHLY);
//js.executeInAndRepeat(r3, 10000L, JobScheduler.YEARLY);
}
}