I don't have a lot to say, but this is my little bit.

Tuesday, September 15, 2009

EnhancedThread makes Java threads more useful


A while back I wrote an application which spawned threads for lengthy import and export operations. I wanted to follow the thread progress using a popup progress bar. I wrote EnhancedThread to help make this possible and easy and generalized.

The most important addition is that a thread's state can be monitored by a listener. An EnhancedThread has the concept of progress, being stopped (paused), and being canceled. A thread also carries a mutable message. These features specifically make it easy to develop a GUI which displays the thread's progress, allows the user to pause, resume and cancel the task, displays messages from the thread to the user.

So in my case, I used an EnhancedThread to import collections of objects, which could take between, say, ten seconds and ten minutes. Along the way, I wanted to display a message indicating what object was currently being imported. Since several import threads could be running concurrently, I wanted to be able to pause, resume, and cancel them, so the user could prioritize their lengthy tasks.

Furthermore, I wanted to have a single window which displayed all threads, but I wanted that window to be monitor my application's master thread pool. The user only needed to see some threads, so EnhancedThread can indicate whether the thread is a user-visible thread, and can also explicitly signal the GUI that the thread manager window should be popped up for this thread. (Of course, that was only a flag, which the GUI could choose to ignore as appropriate.)

import java.util.EventListener;

import javax.swing.*;
import javax.swing.event.*;

/**
*
*
* @author Nicholas R. Rinard
*/
public class EnhancedThread extends Thread {

// ---- v a r i a b l e d e c l a r a t i o n s ---------------------- //

public static final String messageProperty = "__message_property__";
public static final String progressProperty = "__progress_property__";

private String message = "";
private double progress = 0; // negative indicates indeterminate

private boolean canceledFlag = false;
private boolean stoppedFlag = false;

// these public flags (no getters/setters) are clues to the GUI
public boolean isUserVisibleThread = true;
public boolean threadRequestsVisibleWindow = false;
public boolean threadCanBeCanceled = true;

private final EventListenerList allListeners = new EventListenerList();

// ---- p u b l i c a p i -------------------------------------------- //

/**
*
*/
public EnhancedThread( String threadName, double threadProgress, String threadMessage ) {
super( threadName );
this.progress = threadProgress;
this.message = threadMessage;
}

public void addChangeListener( ChangeListener l ) {
if( l != null ) {
allListeners.remove( ChangeListener.class, l );
allListeners.add( ChangeListener.class, l );
}
}

public double getProgress() {
return this.progress;
}

public void setProgress( double newProgress ) {
if( newProgress == this.progress ) return;
this.progress = newProgress;
fireStateChanged();
}

public String getMessage() {
return this.message;
}

public void setMessage( String newMessage ) {
if( newMessage.equals( this.message ) ) return;
this.message = newMessage;
fireStateChanged();
}

public void setCanceledFlag( boolean isCanceled ) {
if( this.canceledFlag == isCanceled ) return;
this.canceledFlag = isCanceled;
fireStateChanged();
}

public boolean getCanceledFlag() {
return this.canceledFlag;
}

public void setStoppedFlag( boolean isStopped ) {
if( this.stoppedFlag == isStopped ) return;
this.stoppedFlag = isStopped;
fireStateChanged();
}

public boolean getStoppedFlag() {
return this.stoppedFlag;
}

public String toString() {
return "Thread{name=" + this.getName() + "; progress=" + this.getProgress() +"; message=" + this.getMessage() + "}";
}

// ---- p r i v a t e c o n v e n i e n c e m e t h o d s ---------- //

private void fireStateChanged() {
EventListener [] listeners = this.allListeners.getListeners( ChangeListener.class );
for( int i = 0; i < listeners.length; i++ ) {
ChangeListener listener = (ChangeListener) listeners[ i ];
ChangeEvent e = new ChangeEvent( this );
listener.stateChanged( e );
}
}
}

No comments:

Post a Comment