org.pealfactory.bronze
Class Tracker

java.lang.Object
  |
  +--org.pealfactory.bronze.Tracker
All Implemented Interfaces:
java.lang.Runnable, Trackable
Direct Known Subclasses:
Composer, Librarian, Tables

public class Tracker
extends java.lang.Object
implements Trackable, java.lang.Runnable

Part of the BronzeAge Java support system, this class provides an implementation of the Trackable interface to provide asynchronous management of lengthy jobs. Support for delegate jobs and worker Thread creation is also provided.

For an example of how Tracker is used, see the Librarian class used by the LibraryApplet to download method libraries. Librarian extends Tracker, allowing it to be used as a Trackable "task" by the applet. Librarian provides the synchronous method getLibrary() which performs the downloading work. The synchronous "worker code" must perform these duties to work effectively as a Trackable:

  1. At the beginning of the task, set the job name setJobName(java.lang.String), set the progress to zero with setProgress(int) and set the total duration of the job setTotalDuration(int). (The total duration can be any integer, and represents the amount of progress reached when the job is finished.)
  2. The code that performs the task must be interspersed with checks for abort and pause status isAborted() and isPaused(). These checks should be performed at least every kRESPONSE_TIME milliseconds, but more often is better - usually there is a suitable inner loop which can be used.
  3. To track progress, setProgress(int) should also be called frequently within the task code, again preferrably in an inner loop. It should monotonically and as evenly as possible increment progress from 0 up to the total duration set at the start of the job.
  4. No special code is needed at the end of the job, except that progress should have reached the total duration value set at the beginning. However if an error occurs during task processing, the setErrorMsg(java.lang.String) method should be called before returning.
In the case of the Librarian, the getLibrary() code contains subtasks. If you examine the loadLib() method you will see a delegate Tracker object is created. This allows one job to be split into subtasks of different durations; the master Tracker handles the calculation of total progress. Here's a simple example where we want to track a job that comprises two subtasks:
setJobName("Master task");
// We'll give 1 progress point to each subtask, so a total of 2.
setTotalDuration(2);
setProgress(0);

// Start 1st subtask - has a total duration of 255
Tracker job1 = new Tracker(255);
job1.setJobName("Subtask 1");
// Start the delegate job, allocating it 1 out of 2 "master job" progress points.
startDelegateJob(job1, 1);
// Here's the "worker" code
for (int i=0; i<255; i++)
{
job1.setProgress(i);
if (isAborted())
return;
// Do stuff
}
// Delegate job finishes - must call this to ensure correct update of master job
endDelegateJob();

// Now start 2nd subtask - has a total duration of 1000
Tracker job2 = new Tracker(1000);
job2.setJobName("Subtask 2");
startDelegateJob(job2, 1);
for (int i=0; i<1000; i++)
{
job2.setProgress(i);
if (isAborted())
return;
// Do stuff
}
endDelegateJob();
Of course it would have been easy enough in this case to have a single task with a duration of 1255. However, using the delegate system makes for simpler code, and also helps in cases where the length of the second subtask can't be determined until the first is complete.

The final service offered by Tracker is the provision of worker threads to help asynchronous monitoring of tasks. Again the LibraryApplet provides a good example of this. The Tracker method startWorker(java.lang.Runnable, java.lang.String) is called by client code to start a worker thread. A Runnable object must be given as a parameter, whose run() method contains the synchronous worker code, and of course usually implements Trackable. The asynchronous Tracker methods isFinished(), pause(), resume() and abortWorker(int) may then be used from client code whilst the task is running in its separate Thread.


Field Summary
private  boolean fAbort
           
private  int fDelegateDuration
           
private  Trackable fDelegateJob
           
private  boolean fError
           
private  java.lang.String fErrorMsg
           
private  java.lang.String fJobName
           
private  boolean fPause
           
private  int fProgress
          0-fTotalDuration
private  java.lang.Runnable fTask
           
private  int fTotalDuration
           
private  java.lang.Thread fWorker
          Asynchronous support.
static java.lang.String kDEFAULT_JOB_NAME
           
static int kRESPONSE_TIME
          The task should check abort and pause status more often than this
 
Fields inherited from interface org.pealfactory.bronze.Trackable
kERROR_NONE
 
Constructor Summary
Tracker(int total)
           
Tracker(int total, java.lang.String name)
           
 
Method Summary
 void abort()
          Abort status treated like an error condition
 void abortWorker(int millisToWait)
           
 void endDelegateJob()
           
 java.lang.String getErrorMsg()
           
 java.lang.String getJobName()
           
 double getProgress()
          0..100.0
 java.lang.String getProgress(int sigFigs)
          Final for speed
 boolean isAborted()
          Final but not synchronized for speed - access is atomic so shouldn't cause a problem.
 boolean isError()
           
 boolean isFinished()
          Only valid after startWorker() called.
 boolean isPaused()
          Final but not synchronized for speed - access is atomic so shouldn't cause a problem.
 void pause()
           
 void reset()
          Starts a new job
 void resume()
           
 void run()
          Only Runnable for private use by worker Thread.
 void setErrorMsg(java.lang.String msg)
           
 void setJobName(java.lang.String name)
           
 void setProgress(int progress)
          Terminates any delegate job! 0...total duration
 void setTotalDuration(int total)
           
 void startDelegateJob(Trackable job, int duration)
           
 void startWorker(java.lang.Runnable task, java.lang.String threadName)
          Use this to start a worker thread to run a task asynchronously.
private  void test()
           
 void waitForResume()
          Remains responsive to aborts whilst paused - should therefore check for abort in caller after this returns.
 
Methods inherited from class java.lang.Object
, clone, equals, finalize, getClass, hashCode, notify, notifyAll, registerNatives, toString, wait, wait, wait
 

Field Detail

kDEFAULT_JOB_NAME

public static final java.lang.String kDEFAULT_JOB_NAME

kRESPONSE_TIME

public static final int kRESPONSE_TIME
The task should check abort and pause status more often than this

fTotalDuration

private int fTotalDuration

fProgress

private int fProgress
0-fTotalDuration

fAbort

private boolean fAbort

fPause

private boolean fPause

fJobName

private java.lang.String fJobName

fError

private boolean fError

fErrorMsg

private java.lang.String fErrorMsg

fDelegateJob

private Trackable fDelegateJob

fDelegateDuration

private int fDelegateDuration

fWorker

private java.lang.Thread fWorker
Asynchronous support.

fTask

private java.lang.Runnable fTask
Constructor Detail

Tracker

public Tracker(int total)

Tracker

public Tracker(int total,
               java.lang.String name)
Method Detail

test

private void test()

getJobName

public java.lang.String getJobName()

setJobName

public void setJobName(java.lang.String name)

setTotalDuration

public void setTotalDuration(int total)

isError

public boolean isError()
Specified by:
isError in interface Trackable

getErrorMsg

public java.lang.String getErrorMsg()
Specified by:
getErrorMsg in interface Trackable

setErrorMsg

public void setErrorMsg(java.lang.String msg)

getProgress

public double getProgress()
0..100.0
Specified by:
getProgress in interface Trackable

getProgress

public final java.lang.String getProgress(int sigFigs)
Final for speed

setProgress

public void setProgress(int progress)
Terminates any delegate job! 0...total duration

startDelegateJob

public void startDelegateJob(Trackable job,
                             int duration)

endDelegateJob

public void endDelegateJob()

abort

public void abort()
Abort status treated like an error condition
Specified by:
abort in interface Trackable

reset

public void reset()
Description copied from interface: Trackable
Starts a new job
Specified by:
reset in interface Trackable

isAborted

public final boolean isAborted()
Final but not synchronized for speed - access is atomic so shouldn't cause a problem.

isFinished

public boolean isFinished()
Only valid after startWorker() called.

pause

public void pause()
Specified by:
pause in interface Trackable

resume

public void resume()
Specified by:
resume in interface Trackable

isPaused

public final boolean isPaused()
Final but not synchronized for speed - access is atomic so shouldn't cause a problem.

waitForResume

public void waitForResume()
Remains responsive to aborts whilst paused - should therefore check for abort in caller after this returns.

startWorker

public void startWorker(java.lang.Runnable task,
                        java.lang.String threadName)
Use this to start a worker thread to run a task asynchronously. The task is assumed to start and run a synchronous Tracker job.

abortWorker

public void abortWorker(int millisToWait)

run

public void run()
Only Runnable for private use by worker Thread.
Specified by:
run in interface java.lang.Runnable