at.spardat.xma.rpc.BusyExecutor Maven / Gradle / Ivy
package at.spardat.xma.rpc;
import java.io.IOException;
import java.io.InputStream;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import at.spardat.xma.page.DialogPage;
abstract public class BusyExecutor {
private Display display;
private Shell shell;
private Canvas canvas;
private static ImageLoader loader;
private int imageNumber = 0;
private final Object semaphor = new Object();
abstract public Object execute() throws Exception;
private Exception exception;
private Object result;
private long lastDraw = 0;
/**
* Load the animated image
*
* @throws IOException
*/
protected void loadImage() {
if (loader == null) {
loader = new ImageLoader();
InputStream is = BusyExecutor.class.getClassLoader().getResourceAsStream("at/spardat/xma/rpc/active-communication.gif");
try {
loader.load(is);
} finally {
if (is != null) {
try {
is.close();
} catch (Exception ex) {
throw new RuntimeException("Can not close image loader", ex);
}
}
}
}
}
/**
* Start the execution of the job.
*
* Exceptions happening in execute() will be stored in the field
* exception, and can be queried with getException(). No exception
* is thrown here.
*
* The result of the execute method can later be retrieved by
* getResult().
*
* @return The result of the execute method
*/
public Object startExecutor() {
display = BusyExecutorHelper.getDisplay();
BusyIndicator.showWhile(display, new Runnable() {
public void run() {
try {
// Execute job and show busy indicator
ExcuteThread executeThread = new ExcuteThread();
Shell activeShell = null;
// Show animation after a while
synchronized (semaphor) {
executeThread.start();
if (display != null) {
activeShell = display.getActiveShell();
}
semaphor.wait(2000);
if (executeThread.running) {
if (activeShell != null) {
shell = new Shell(activeShell, SWT.APPLICATION_MODAL | SWT.BORDER);
} else {
shell = new Shell(display, SWT.APPLICATION_MODAL | SWT.BORDER);
}
}
}
if (shell != null) {
showBusyShell(activeShell, executeThread);
}
setResult(executeThread.getResult());
setException(executeThread.getException());
} catch (InterruptedException e) {
// Sleep interrupted
}
}
});
return getResult();
}
protected void showBusyShell(Shell parentShell, ExcuteThread executeThread) {
loadImage();
shell.setLayout(new FillLayout());
canvas = new Canvas(shell, SWT.NONE);
canvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
Image image = new Image(display, loader.data[imageNumber]);
event.gc.drawImage(image, 0, 0);
image.dispose();
}
});
/* Open the shell and start the animation */
shell.pack();
//shell.layout();
DialogPage.centerShell(parentShell, shell);
shell.open();
new AnimationThread().start();
BusyIndicator.showWhile(display, new Runnable() {
public void run() {
while (!shell.isDisposed()) {
// animate every 200ms at maximum.
if (System.currentTimeMillis() - lastDraw > 200) {
imageNumber = imageNumber == loader.data.length - 1 ? 0 : imageNumber + 1;
canvas.redraw();
lastDraw = System.currentTimeMillis();
}
if (BusyExecutorHelper.hasDisplayMessages()) {
// dispose and breake if there is an SWT runnable
// in order to not break XMA conventions.
shell.dispose();
break;
}
// this here is small gap in the solution:
// when between the check and readAndDispatch() someone schedules a task
// it will be executed in readAndDispatch() agains our will.
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
});
// check if we are really ready. This is necessary because of the early breaks above
synchronized (semaphor) {
while (executeThread.running) {
try {
semaphor.wait(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public Exception getException() {
return exception;
}
private void setException(Exception exception) {
this.exception = exception;
}
public Object getResult() {
return result;
}
private void setResult(Object result) {
this.result = result;
}
private class ExcuteThread extends Thread {
private boolean running = true;
private Exception exception;
private Object result;
public void run() {
try {
result = execute();
} catch (Exception e) {
exception = e;
} finally {
synchronized (semaphor) {
running = false;
semaphor.notifyAll();
}
display.syncExec(new Runnable() { // NOTE: this asynchexec already stops the event loop
public void run() {
if (shell != null) {
shell.dispose(); // ... this is thus only to make it sure.
display.wake();
}
}
});
}
}
public Exception getException() {
return exception;
}
public Object getResult() {
return result;
}
}
class AnimationThread extends Thread {
public void run() {
while (!shell.isDisposed()) {
display.wake(); // animate in the main event loop, only trigger a wake here
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}