Skip to content

Progress Object Pattern

When a user executes a long running background task an application usually wants to report progress about the task to the user. Application developers often do this by passing a ProgressListener to the background task’s  method and the method uses the ProgressListener to report it’s progress. But you can also design it in another way and I would like to introduce another pattern here. At least I would like to show an example of how to implement a progress object pattern with Javas Swing. The complete example code is available at gitthub: https://github.com/link-intersystems/blog/tree/master/progress-object-pattern.

But before I introduce the progress object patten let us take a look at the progress listener concept.

Progress listener

A progress listener is conceptually a callback that a background task can use to report it’s progress. Thus you usually first define a ProgressListener interface.

public interface ProgressListener {
    public void start(int totalWork);
    public void worked(int worked);
    public void finished();
}

Since the ProgressListener is just an interface clients can adapt specialized implementations. E.g. a StdoutProgressListener

public class StdoutProgressListener implements ProgressListener {

    private int totalWork = 0;
    private int worked = 0;

    public void start(int totalWork){
         this.totalWork = totalWork;
         this.worked = 0;
         printProgress();
    }

    public void worked(int worked){
        this.worked += worked;
        printProgress();
    }

    public void finished(){
        this.worked = totalWork;
        printProgress();
    }

    private void printProgress(){
         String progress = MessageFormat.format("{0} / {1}", worked, totalWork);
         System.out.println(progress);
    }

}

Background tasks will then take a ProgressListener  as parameter so that they can report progress. E.g.

public class AsyncTask {

    public Future<Integer> execute(ProgressListener progressListener) {
       Callable<Integer> someProgressCallable = new SomeProgressCallable(progressListener);
       FutureTask futureTask = new FutureTask(someProgressCallable);

       ExecutorService es = ...;
    
       es.submit(futureTask);
       
       return futureTask;
    }
}

public class SomeProgressCallable implements Callable<Integer> {

    private ProgressListener progressListener;
    
    public SomeProgressCallable(ProgressListener progressListener){
         this.progressListener = progressListener;
    }

 
    public Integer call() throws Exception {
        Integer result = ...;
        progressListener.start(...); 
        try { 
            while(....){ 
               ... 
               progressListener.worked(1); 
            } 
        } finally { 
            progressListener.end(); 
        }
        return result;
    }
}

In this case a client must create an instance of a ProgressListener and pass it to the task.

Progress object pattern

Some time ago I thought about other options of how to implement progress reporting in an application and came accross another pattern that I call the progress object pattern. Let me summerize my thoughts that lead to that pattern.

In Java 1.5 the java.util.concurrent.Future<T> interface was introduced to represent a reference to an object that will be available in the future. This means that there is some kind of process that must be finished and the result is that future object. Well, if the process knows all of the actions it must do to create the object it can report progress.

To summerize this:

A process is a series of steps or actions that must be done in order to achieve a goal.
Progress is the knowlede about a process’s state

A Future object is a reference to an object that will be available in the future.  A progress is the state of a process that will produce that future object. Thus we can design a progress as an extension of a Future by introducing a new type called Progress<T> extends Future<T>. E.g.

Progress Object Pattern

A method that can report progress must now just change it’s return type to Progress<T>. So if a method returns a Progress<T> it can report progress and if it returns a Future<T> it can’t. E.g.

public class SomeService {

    public Progress<String> executeTaskWithProgress(....) {
        ...
    }


    public Future<String> executeTaskWithoutProgress(....) {
        ...
    }

}

Code evolution of Progress Listener vs. Progress Object

Lets take a look at the impacts that code evolution from a non-progress method to a progress method has using either a ProgressListener or a ProgressObject.

Code evolution with a ProgressListener

public class MathService {

   public Future<BigDecimal> computePi(int digits) {
        
   }
}

To make the change downwardly compatible you must introduce a new method that supports progress and delegate calls from the former method to that method.

The implementation of the progress method is easier if you use a null object pattern in the delegate method instead of passing a null reference.

public class MathService {

   public Future<BigDecimal> computePI(int digits){
      return computePI(digits, new NullProgressListener());
   }

   public Future<BigDecimal> computePI(int digits, ProgressListener progressListener){
       // ....
   }

}

Code evolution with a Progress Object

public class MathService {

   public Future<BigDecimal> computePi(int digits) {
        ...
   }
}
public class MathService {

   public Progress<BigDecimal> computePi(int digits) {
        ...
   }
}

Differences between a progress listener and progress object

The main difference is the instantiation of the progress reporting object. When you use a ProgressListener callback, client code must instantiate it and pass it to a method that can report progress and the progress method must change it’s signature to take a ProgressListener parameter. Changing the parameters of a method can make it incompatible with former legal client code or you introduce a delegate method as shown above.

Progress Listener: former client code

MathService mathService = ...;
int piDigits = ...;

Future<T> pi = mathService.computePi(piDigits);

Progress Listener: evolved client code

MathService mathService = ...;
int piDigits = ...;
ProgressListener progressListener = ...;

Future<T> pi = mathService.computePi(piDigits, progressListener);

Progress Object: former client code

MathService mathService = ...;
int piDigits = ...;

Future<String> pi = mathService.computePi(piDigits);

Progress Object: evolved client code

MathService mathService = ...;
int piDigits = ...;

Progress<String> piProgress = mathService.computePi(piDigits);

Since I extend Progress<T> from Future<T> a client that is not interessted in progress can simply use the Future<T> interface while another client uses the Progress<T>.

Progress Object state change notification

Just replacing a Future<T> with Progress<T> is not enough to get informed about progress state changes. But fortunately we can solve this problem easy using the Java Beans API. The Progress<T> must support property change events. E.g.

Progress Property Change Support

 

MathService mathService = ...;
int piDigits = ...;

Progress<String> piProgress = mathService.computePi(piDigits);

StdoutProgressReporter progressReporter new StdoutProgressReporter();
piProgress.addPropertyChangeListener(progressReporter);

Real life Progress Object example with Java Swing

In Java Swing a SwingWorker is usually used to execute background tasks. Fortunately a SwingWorker implements a Future. A SwingWorker also has a progress property so half of the work is already done. Thank’s to the designers of SwingWorker and the Java API.

So let’s design the interfaces in smart way so that we can easity adapt a SwingWorker.

public interface Progress<V> extends Future<V>, PropertyChangeSupported {

	public int getProgress();

}

public interface PropertyChangeSupported {
	public void addPropertyChangeListener(PropertyChangeListener propertyChangeListener);
	public void removePropertyChangeListener(PropertyChangeListener propertyChangeListener);
}

Now that we have the interfaces we can implement a ProgressWorker<T> that extends a SwingWorker and makes it a Progress<T>.

public abstract class ProgressWorker<T> extends SwingWorker<T, Void> implements Progress<T> {
}

Since a SwingWorker already supports PropertyChangeListeners  and fires “progress” property change events  we are done. Of couse you must not implement a ProgressWorker. It can also be done just using a SwingWorker but the additional Progress interface adds  more flexibility in application design and classes that only depend on the Progress interface are more easier to unit test.

At least we need an Action that is responsible for

  • getting user data from UI models
  • execute something that returns a Progress
  • bind the Progress to everyone who is interessted in progress
  • get the result of the progress and update UI models

In order to get notified about the SwingWorker‘s state I implemented an adapter class that other can subclass – a SwingWorkerPropertyChangeAdapter.

public class SwingWorkerPropertyChangeAdapter implements PropertyChangeListener {

	@Override
	public void propertyChange(PropertyChangeEvent evt) {
		String propertyName = evt.getPropertyName();
		if ("state".equals(propertyName)) {
			StateValue state = (StateValue) evt.getNewValue();
			switch (state) {
			case PENDING:
			case STARTED:
				started();
				break;
			case DONE:
				SwingWorker<?, ?> future = (SwingWorker<?, ?>) evt.getSource();
				boolean cancelled = future.isCancelled();
				done(cancelled);
			}
		}
	}

	protected void started() {
	}

	protected void done(boolean cancelled) {
	}

}

The ProgressAction implementation I choose is this:

public abstract class ProgressAction<T> extends AbstractAction {

	private static final long serialVersionUID = 2088582900434640623L;

	private class SwingWorkerPropertyAdapter extends SwingWorkerPropertyChangeAdapter {

		@Override
		protected void done(boolean cancelled) {
			ProgressAction<?> progressAction = ProgressAction.this;
			progressAction.workerDone();
		}
	}

	private SwingWorkerPropertyAdapter swingWorkerPropertyAdapter = new SwingWorkerPropertyAdapter();
	private Collection<ProgressAware> progressAwares = new LinkedHashSet<ProgressAware>();

	private Progress<T> progress;

	public void addProgressAware(ProgressAware progressAware) {
		this.progressAwares.add(progressAware);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		setEnabled(false);

		ProgressWorker<T> progressWorker = createProgressWorker();
		setProgress(progressWorker);
		progressWorker.execute();
	}

	protected abstract ProgressWorker<T> createProgressWorker();

	private void setProgress(Progress<T> progress) {
		this.progress = progress;

		for (ProgressAware progressAware : progressAwares) {
			progressAware.setProgress(progress);
		}

		progress.addPropertyChangeListener(swingWorkerPropertyAdapter);
	}

	void workerDone() {
		progress.removePropertyChangeListener(swingWorkerPropertyAdapter);
		try {
			done(progress);
		} finally {
			progress = null;
			setEnabled(true);
		}
	}

	protected void done(Future<T> result) {
	}

}

And for everyone that is interrested in progress I added a ProgressAware interface.

public interface ProgressAware {
	public void setProgress(Progress<?> progress);
}

You can get the complete example code on github: https://github.com/link-intersystems/blog/tree/master/progress-object-pattern

progress-object-swing-example

 

Leave a Reply

Your email address will not be published. Required fields are marked *

 

GDPR Cookie Consent with Real Cookie Banner