How to report progress from long-running tasks

Using progress monitor arguments

The class com.bc.ceres.ProgressMonitor is generally used to report progress in log running tasks. Many framework callbacks provide an instance of this interface to client functions.

void archiveInternet(ProgressMonitor pm) {
	try {
    	pm.beginTask("Archiving internet", 100);
		// Here: perform 20% of the work (download)
		pm.worked(20);
		// Here: perform 50% of the work (zip)
		pm.worked(50);
		// Here: perform remaining 30% work (copy)
		pm.worked(30);
	} finally {
		pm.done();
	}
}

Or pass sub-progess monitors to sub-functions:

void archiveInternet(ProgressMonitor pm) {
	try {
    	pm.beginTask("Archiving internet", 100);
		// Here: perform 20% of the work (download)
		downloadInternet(SubProgressMonitor.create(pm, 20));
		// Here: perform 50% of the work (zip)
		zipDownloadedInternet(SubProgressMonitor.create(pm, 50));
		// Here: perform remaining 30% work (copy)
		copyInternetZip(SubProgressMonitor.create(pm, 30));
	} finally {
		pm.done();
	}
}

Not shown in the code above is the check for cancelation. A user may have requested to cancel a long-running task. This is reported through the ProgressMonitor.isCanaceled() method. 

void archiveInternet(ProgressMonitor pm) {
	...
	for (...) {
		...
		if (pm.isCancelled()) {
		 	return; // or throw new CanceledException();
		}
        ...
	}
	...
}

Calling of methods that take a progress monitor argument 

If you can't or don't want to report progress you can simply pass a null progress monitor (but don't pass null):

 

archiveInternet(ProgressMonitor.NULL);

From command-line tools, you can use:

archiveInternet(new PrintWriterProgressMonitor(System.out));

From GUI code, we ustilise the adapter class org.esa.snap.rcp.util.ProgressHandleMonitor to report progress through ProgressMonitor interface but using NetBeans progress bars. As long running tasks should not be called from the Swing event dispatcher thread (EDT), we will have to create or reuse background threads to do so. The NetBeans org.netbeans.api.progress.ProgressUtils API provides some useful methods for this:

ProgressHandleMonitor pm = ProgressHandleMonitor.create("Archiving internet");
Runnable operation = () -> {
	archiveInternet(pm);
};
ProgressUtils.runOffEventThreadWithProgressDialog(operation, "Archiving internet", 
                                                  pm.getProgressHandle(), 
                                                  true, 
                                                  50,  // time in ms after which wait cursor is shown
                                                  1000);  // time in ms after which dialog with "Cancel" button is shown

Or using the NetBeans class org.openide.util.RequestProcessor:

RequestProcessor.getDefault().post(() -> {
	ProgressHandle handle = ProgressHandleFactory.createHandle("Archiving internet");
	ProgressMonitor pm = new ProgressHandleMonitor(handle);
	archiveInternet(pm);
});

See https://github.com/senbox-org/snap-desktop/blob/master/snap-rcp/src/main/java/org/esa/snap/rcp/util/ProgressHandleMonitor.java.

Documentation of NetBeans Progress API:

 

In older code you will find uses of the com.bc.ceres.swing.progress.ProgressMonitorSwingWorker class. Don't use it as it will cause the main frame to go into background and let other desktop windows hide it. Very annoying for users (and us developers).