Eve Application Development

Michael L Brereton - 02 January 2008, http://www.ewesoft.com/

 

Beginners Guide - Contents

<< Previous: GUI Data Editing

>> Next: Dynamic Controls

Handles and Tasks

Eve Application Development

Handles and Tasks

TimeOut Objects

Handles

Handle State

Handle Progress

Other Handle methods

Return Values

Tasks

Monitoring a Handle/Task with a ProgressBarForm

 

 

Many of the methods in the Eve library use or return eve.sys.Handle Objects. It is important to understand how they work and how to use them.

TimeOut Objects

An eve.sys.TimeOut object specifies a length of time allowed for some operation to execute with millisecond precision. You create a TimeOut object by calling its constructor with the length of time in milliseconds as the parameter. Once this is done you can call the following methods on it:

 

There are also two constant TimeOut objects that are predefined – TimeOut.Forever which never expires, and TimeOut.Immediate which is always expired. These are useful when you want to either wait indefinitely or not at all.

Handles

 

A Handle is an object that allows you to monitor the status of an arbitrary asynchronous task. A task will set various flags and values on a handle to give an indication as to the progress and status of the running task. Any thread can monitor the progress and status of the running task by polling the handle or by waiting for certain conditions to occur.

Handle State

The state of a Handle is used to report whether the task the Handle is monitoring is running or has stopped, whether it was successful or if it failed, and when the task has attained certain critical points along its progress. The state of the Handle is really a 32-bit integer with each bit representing a different condition. The top six bits are reserved and are defined to be: Changed (indicates some kind of change of state), Running (indicates that the task has begun and is still running), Stopped (indicates that the task has stopped and no further state changes will occur), Success (indicates that the task has successfully done what it had set out to do), Failure (indicates that the task failed to do what it was supposed to), Aborted (indicates that the task was aborted due to an outside – possibly user – intervention). All other bits are free to be programmer defined.

 

A running task can change the state of a Handle by calling set(int newState) or by calling setFlags(int bitsToSet, int bitsToClear). A monitoring thread can call the check() method to check the current state of the handle. This is a non-blocking call which simply returns the current state of the handle.

 

A Thread can call one of the many waitOn()  methods to wait until certain bits of the Handle state have been set. Check the API for a description of the various waitOn() methods. A Thread can also call waitUntilStopped() or waitUntilCompletion() to wait for the handle to report that it has stopped.

Handle Progress

The progress of a Handle is meant to represent a fractional value that indicates how close the task is to completion. A value of 0.0 indicates that the task has now begun and a value of 1.0 indicates that the task is complete. This value is only an indication of the task progress and should not be used to determine the running/stopped state of the task. During the execution of a Task the progress may be reset to zero several times due to the execution of sub-tasks within the main task.  The progress of a Handle is set using setProgress(float progress). Using this method ensures that any Threads monitoring the task gets alerted to the change in progress. You can also use a value of -1 which is used to indicate that work is being done even though the exact progress towards completion may be unknown. If a ProgressBar control is monitoring the Handle setProgress(-1) causes the ProgressBar to show a small “bouncing” block instead of a steadily increasing bar.

 

An additional variable – doing is also accessible. This is a String that can be used to give an indication as to what the task is doing at any point in time. Use of this variable is completely optional. The method startDoing(String task) is a convenient way to set the progress to zero and set the doing variable at the same time.

Other Handle methods

 

The void stop(int reason) method is used to request that the task be stopped. However whether or not this works depends on the Handle and the task being monitored. You should never assume that as soon as this method is called the task will stop. You should always wait until the Stopped bit has been set.

Return Values

A Handle can also be used to return a value from the asynchronous task. The task can set the returnValue variable to be any kind of object. There is also an error value that can be set to a Throwable Object if an error occurs in processing that may have caused it to abort operations. Usually the Handle will set the Failed status bit after setting the error value.

 

The method succeed(Object returnValue) is a convenience method used to set the returnValue and then set the flags to Stopped|Success. The method fail(Throwable t) is a convenience method used to set the error value and then set the flags to Stopped|Failure.

Tasks

The class eve.sys.Task can be thought as a Handle that can run itself within a new Thread or it can be thought of as a Thread with its own Handle (itself). To create a new Task extend the Task Object and then override the method doRun(). The start() method of Task will ensure that it first sets the Running status bit before calling doRun(). When doRun() returns it will automatically ensure that the Running bit is cleared and that the Stopped bit is set.

Monitoring a Handle/Task with a ProgressBarForm

 

The example below shows how to use a Task to do a process in a separate Thread and monitor the progress of the process.

 

package evesamples.ui;
 
import java.io.IOException;
 
import eve.sys.Handle;
import eve.sys.Task;
import eve.ui.Button;
import eve.ui.Form;
import eve.ui.MessageBox;
import eve.ui.ProgressBarForm;
import eve.ui.ReportException;
 
public class TestProgress extends Form{
 
//
// Do some work in the current Thread.
//
public String doSomething(Handle h) throws IOException
{
     if (h == null) h = new Handle();
     h.startDoing("Preparing...");
     int max = 10;
     for (int i = 0; !h.shouldStop && i<max; i++){
             //
             // Should do some real work here. 
             //
             try{Thread.sleep(300);}catch(InterruptedException e){}
             h.setProgress((float)(i+1)/max);
     }
     if (h.shouldStop) return null;
     h.startDoing("Doing...");
     for (int i = 0; !h.shouldStop && i<max; i++){
             try{Thread.sleep(100);}catch(InterruptedException e){}
             h.setProgress((float)(i+1)/max);
     }
     if (h.shouldStop) return null;
     // Fake an error.
     if ((int)(Math.random()*2) == 0) throw new IOException("Fake error!");
     return "Did my work!";
}
//
// Create a Handle to run doSomething() in a separate Thread (Task).
// You must call start() on the returned Handle to begin the process.
//
public Handle doSomethingInThread()
{
     return new Task(){
             protected void doRun(){
                     try{
                            String got = doSomething(this);
                            // If doSomething() returned null, then
                            // the stop() method was called on this task
                            // which set the shouldStop field to true.
                            if (got == null) set(Aborted|Stopped);
                            else succeed(got);
                     }catch(IOException e){
                            fail(e);
                     }
             }
     };
}
 
public TestProgress()
{
     title = "Test Progress";
     maximizeOnPDA();
     Button b = new Button("Do something now.");  
     addLast(b).setCell(HSTRETCH);
     b.action = "doSomething";
     doButtons(CANCELB);
}
 
public boolean handleAction(String action)
{
     if (action.equals("doSomething")){
             Handle h = doSomethingInThread();
             ProgressBarForm pbf = new ProgressBarForm();
             pbf.title = "Progress...";
             pbf.maximizeOnPDA();
             pbf.setMainTask("Doing something!");
             pbf.showStop = true;
             pbf.horizontalLayout = false; //Put cancel below the bar.
             Object got = pbf.executeAndGetReturnValue(h);
             if (got == null){
                     if (h.error != null)
                            new ReportException(h.error).execute();
                     else
                            new MessageBox("Aborted","You aborted the process!",MessageBox.MBOK).execute();
             }else{
                     new MessageBox("Success","Process returned: "+got,MessageBox.MBOK).execute();
             }
             return true;
     }
     return super.handleAction(action);
}
 
}

 

The method doSomething(Handle h) performs a lengthy task in the current Thread. It returns a String on success, or null if the stop() method is called on the provided Handle (because h.shouldStop will be set true). It can also throws an IOException (we will fake one randomly).

 

Doing the method doSomethingInThread() wraps the doSomething() method within a Task. When start() is called on the returned Handle (which is a Task in this case) it calls the run() method on the Task which then calls doRun(). The doRun() method calls the doSomething() method, passing itself as the Handle. If doSomething() returns a valid String it reports success and sets the returnValue to be the returned String by calling succeed(). If an exception is thrown it calls fail() to set the error value to that error. If doSomething() returns null it can only mean that the stop() method was called on the Task and so it sets the Aborted and Stopped bits.

 

The handleAction() method actually starts the task running and monitors it using a ProgressBarForm. There are a number of useful methods in ProgressBarForm one of which is executeAndGetReturnValue(Handle h). This method calls start() on the Handle (if it is already started this has no effect) and then monitors and displays the progress of the Handle. When complete it will return the returnValue of the Handle if any. Our code then checks that return value and if it is null it checks the error value to determine if this is due to an Exception or due to the user aborting.