/**
* The XMOJO Project 5
* Copyright 2003 XMOJO.org. All rights reserved.
* NO WARRANTY
* BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
* THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
* OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
* PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
* OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
* TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE
* LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
* REPAIR OR CORRECTION.
* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
* ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
* THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
* GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
* USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF
* DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
* PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE),
* EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGES.
**/
package javax.management.timer;
import java.util.*;
import java.io.*;
/**
* A scheduler for running tasks in a Java VM.
* This class can be used to schedule a particular task at a
* specified time. This is basically to control effectively a number of tasks
* using a limited number of threads
*/
public class Scheduler extends Thread {
/**
* The maximum numer of threads to be used in executing tasks for this
* scheduler
*/
int MAX_THREADS = 4;
/*
* To get the number of threads that this scheduler controls
*/
public int getMaxThreads() {
return MAX_THREADS ;
}
private static int DEFAULT_MAX_THREADS = 4;
/*
* In case a scheduler is initialized without specifying the max number
* of threads neither in the conf file , nor in the arguments passed
* to the method , then what should the default number
*/
public static int getDefaultMaxThreads() {
return DEFAULT_MAX_THREADS ;
}
/*
* In case a scheduler is initialized without specifying the max number
* of threads neither in the conf file , nor in the arguments passed
* to the method , then what should the default number
*/
public static void setDefaultMaxThreads(int i) {
if ( (i>=0) && (i<100) ) {
DEFAULT_MAX_THREADS=i;
}
}
/*
* To set/change the number of threads that this scheduler controls .
* Note this can be done only when the scheduler has been stopped by
* the stopThis or stopAll methods .
* The boolean returned will indicate whether the operation was successful
* or not .
*/
public boolean setMaxThreads(int i) {
if (isAlive()) return false;
if ( (i>=0) && (i<100) ) {
MAX_THREADS=i;
return true ;
}
return false ;
}
/*
* The total number of threads which are scheduled via schedulers . that is
* the combined total of all active schedulers .
*/
static int TOTAL_THREADS = 0;
/*
* To get the total number of active threads that all the schedulers
* put together control.
*/
public static int getTotalThreads() {
return TOTAL_THREADS ;
}
// The boolean for one to be able to stop all schedulers
static boolean STOP_ALL = false;
// The boolean for one to be able to stop this scheduler alone
boolean STOP_THIS = false;
// the number of threads that have been stopped for this scheduler alone
int NUM_THREADS_STOPPED=0;
Vector runnables = new Vector(); // The tasks to be run
Vector times = new Vector(); // when to run them
Vector workers = new Vector(); // the guys who do the work
Vector ready_tasks = new Vector(); // the tasks to be run immediately
// We will store internally reference to all the schedulers when they
// are instantiated . Schedulers can no longer be instantiated publicly
private static Hashtable schedulers = new Hashtable(15) ;
private static Hashtable maxThreads = new Hashtable(15);
// The conffile which should be used to read the details of the number
// of threads etc .
private static String confFile = "conf/threads.conf" ;
private static boolean readConfFile = false ;
public static String getConfFile() {
return confFile;
}
public static void setConfFile(String s) {
confFile=s;
}
private static synchronized void readTheConfFile() {
if (readConfFile) return;
BufferedReader is = null;
String line = null;
try {
File ff = new File(confFile);
if (!ff.exists()) {
//System.err.println(" in 135 Scheduler "+confFile+" does not exist");
readConfFile=true;
return ;
}
is = new BufferedReader(new FileReader(ff));
while ( (line = is.readLine()) != null) {
if (line.trim().equals("")) continue;
else if (line.startsWith("#")) continue;
else {
StringTokenizer tok = new StringTokenizer(line);
if (tok.countTokens()==2) {
String s = tok.nextToken();
int numb =-1;
try {
numb =Integer.parseInt(tok.nextToken());
} catch ( NumberFormatException nfe) {}
if ( (numb<0) || (numb>100) ) {
System.err.println(" Invalid line in the conf file "+
confFile+
" :"+line);
continue;
}
maxThreads.put(s,new Integer(numb));
} else {
System.err.println(" Invalid line in the conf file "+confFile+
" :"+line);
}
}
}
} catch (IOException e) {
System.err.println("Scheduler File read Error:"+confFile + ": "+e);
} catch (SecurityException anye) {}
readConfFile = true ;
}
public static Scheduler createScheduler(String nam) {
return createScheduler(nam,-1);
}
/*
* This is the method to create a new instance of the Scheduler
* and give it . If there already exists a scheduler with that name
* then that is returned .
*/
public static Scheduler createScheduler(String nam,int maxThreadNumber) {
synchronized (schedulers) {
if (nam==null) return null;
Scheduler sch = getScheduler(nam);
if (sch!=null) return sch ;
readTheConfFile();
if ((maxThreadNumber<=0) || (maxThreadNumber>100) ) {
Integer integ = (Integer)maxThreads.get(nam);
if (integ!=null) sch =new Scheduler(nam,integ.intValue());
else sch =new Scheduler(nam);
} else sch =new Scheduler(nam,maxThreadNumber);
schedulers.put(nam ,sch);
//commentSystem.out.println("Instantiated "+sch.getName()+" scheduler with "+
//comment sch.MAX_THREADS +" threads ");
return sch;
}
}
/*
* One should use this method to get a reference to the scheduler . If
* a scheduler with the given name does not exist then it returns null;
* I agree this method is dangerous in the sense that malicious code can call
* this method get reference and say suspend all scheduling .So should be
* used with care
*/
public static Scheduler getScheduler(String nam) {
if (nam==null) return null;
return (Scheduler)schedulers.get(nam);
}
/**
* The constructor initializes the worker threads which invokes the task
* to be scheduled at a specific time.
*/
private Scheduler(String name,int maxThreads)
{
super(name);
MAX_THREADS = maxThreads;
}
/**
* The constructor initializes the worker threads which invokes the task
* to be scheduled at a specific time.
*/
private Scheduler(String name)
{
this(name,DEFAULT_MAX_THREADS);
}
/**
* This methood schedules a one-time task at the specified time
*/
public synchronized void scheduleTask(Runnable task, Date when)
{
if (when == null) when = new Date();
for (int i=0;i<times.size();i++) {
Date d = (Date) times.elementAt(i);
if (d.after(when)) {
times.insertElementAt(when,i);
runnables.insertElementAt(task,i);
return;
}
}
times.addElement(when);
runnables.addElement(task);
notifyAll();
}
/**
* This methood is used to remove a task from being scheduled.
*/
public synchronized void removeTask(Runnable task)
{
if (task == null) return;
for (int i=0;i<runnables.size();i++) {
Runnable r = (Runnable) runnables.elementAt(i);
if (task.equals(r)) {
runnables.removeElement(r);
times.removeElementAt(i);
i--;
}
}
}
/*
* If you have used stopAll method to suspend all schedulers , then using this
* will restart all of them
*/
public boolean resumeAll() {
synchronized (schedulers) {
if (!STOP_ALL) return false;
STOP_ALL=false;
for (Enumeration en=schedulers.elements();en.hasMoreElements();) {
Scheduler sch = (Scheduler)en.nextElement();
sch.start();
}
}
return false ;
}
//Stops and destroys the schduler and its worker threads
//Cannot reuse scheduler when this method is called
public void killScheduler()
{
if (STOP_ALL) return ;
STOP_THIS= true;
STOP_ALL = true;
try{
for (int i=0;i<workers.size();i++) {
WorkerThread worker = (WorkerThread) workers.elementAt(i);
worker.wakeUp();
}
}catch(Throwable th){th.printStackTrace();}
//waits at the max 1sec for the this scheduler thread to die
for(int i = 0; i < 50; i++) {
if(isAlive())
{
try{
Thread.sleep(20);
}catch(Exception e){}
}
else
break;
}
}
/*
* This method will stop not only this but all schedulers threads .
* so be very careful in using this .
* Will return false if it could not stop any thread
*/
public static boolean stopAll()
{
synchronized (schedulers) {
if (STOP_ALL) return false;
int count=0;
STOP_ALL = true;
int totalorg =TOTAL_THREADS;
while (TOTAL_THREADS>0)
{
if(count >= STOP_TIME_OUT) {
System.err.println("Schedulers did not stop properly: "
+ (totalorg-TOTAL_THREADS) +
" threads stopped out of " +
totalorg);
System.err.println("The remaining "+
TOTAL_THREADS+
" threads did not stop in "+
STOP_TIME_OUT+ " seconds ");
return false;
}
try {
Thread.sleep(1000);
count++;
}
catch (Exception e){}
}
System.out.println((totalorg-TOTAL_THREADS) +
" of the "+totalorg+
" active threads in the control "+
" of the schedulers stopped");
TOTAL_THREADS=0;
return true;
}
}
/*
* This method will stop this scheduler alone . The scheduler will not
* clean up the runnables queue by calling this method . One can restart
* by invoking the start method . If you want this scheduler to get
* cleaned up invoke the cleanup method after invoking this method
* Will return false if it could not stop any thread
*/
public boolean stopThis()
{
int count=0;
STOP_THIS = true;
while (NUM_THREADS_STOPPED < MAX_THREADS)
{
if(count >= STOP_TIME_OUT) {
System.err.println("Scheduler:"+getName()+" did not stop properly: " + NUM_THREADS_STOPPED + " threads stopped out of " + MAX_THREADS);
System.err.println("The remaining "+
(MAX_THREADS-NUM_THREADS_STOPPED)+
" threads of scheduler:"+getName()+
"did not stop in "+
STOP_TIME_OUT+ " seconds ");
return false;
}
try {
Thread.sleep(1000);
count++;
}
catch (Exception e){}
}
System.out.println(NUM_THREADS_STOPPED + "out of "+MAX_THREADS+
" active threads stopped in "+
" Scheduler:"+getName());
return true;
}
/*
* This method should be used if you want to cleanup the scheduler and
* release all resources from it. Remember the external code has to take
* care of the references it has to this object .This should be called
* when you do a stopThis on a scheduler and you have no intentions
* on using it (that is restarting it) again .
*/
public boolean cleanUp() {
//if (isAlive())
//{
// System.out.println(" *********** Returning from cleanUp isAlive is true "+this.getName());
// return false;
//}
synchronized (schedulers) {
times.removeAllElements();
runnables.removeAllElements();
ready_tasks.removeAllElements();
workers.removeAllElements();
schedulers.remove(getName());
return true;
}
}
static int STOP_TIME_OUT = 15;
/*
* The time in seconds that the calling thread should wait while invoking
* the stopALl or stopThis methods . That is when these two methods are
* invoked the method will block for a maximum of this time before
* printing a message that all the threads could not be stopped . Of course
* if all the releveant threads stop then the method will return immediately
*/
public void setStopTimeout(int timeout)
{
STOP_TIME_OUT = timeout;
}
/** something to let this thread rest when nothing to do **/
synchronized Runnable getTheWork() {
while (times.size() == 0) try {
wait(10);
if ( (STOP_ALL) || (STOP_THIS) )return null;
} catch (InterruptedException iex) {}
Date first = (Date) times.firstElement();
Date now = new Date();
if (first.after(now)) return null;
Runnable task = (Runnable) runnables.firstElement();
runnables.removeElement(task);
times.removeElement(first);
return task;
}
/** The main thread which kicks off the task execution **/
public void run() {
synchronized (schedulers) {
STOP_ALL=false;
STOP_THIS=false;
}
startWorkers(); // first start the workers
if (MAX_THREADS==0) return ;
while ( (!STOP_ALL) && (!STOP_THIS) )
try {
Runnable task = getTheWork();
if (task == null) {
try {
waitSchedule();
} catch (InterruptedException iex) {}
} else {
startTask(task);
}
} catch (Exception ex) {
System.err.println("Exception scheduling task in scheduler:"
+getName()+" "+ ex);
// ex.printStackTrace();
}
return;
}
synchronized void waitSchedule() throws InterruptedException {
wait(10);
}
/** start the task **/
synchronized void startTask(Runnable task) {
ready_tasks.addElement(task);
for (int i=0;i<workers.size();i++) { // first start the workers
WorkerThread worker = (WorkerThread) workers.elementAt(i);
worker.wakeUp();
}
}
/** get the next task ready to run **/
synchronized Runnable getNextTask() {
if (ready_tasks.size() == 0) return null;
Runnable task = (Runnable) ready_tasks.firstElement();
ready_tasks.removeElement(task);
return task;
}
/** start the workers**/
synchronized void startWorkers() {
if ( (STOP_ALL) || (STOP_THIS) ) return;
workers = new Vector();
for (int i=0;i<MAX_THREADS;i++) {
TOTAL_THREADS += 1;
WorkerThread worker = new WorkerThread(this,getName()+"-"+(i+1));
workers.addElement(worker);
worker.start();
}
}
public void deregisterThisScheduler(String nam)// Devesh...because of reload problem in JavaUI
{
if(nam == null)
return;
else
{
Scheduler s = (Scheduler)schedulers.remove(nam);
s.stopAll();
}
}
}
|