Skip to content

Instantly share code, notes, and snippets.

@mpilone
Last active January 30, 2019 16:36
Show Gist options
  • Select an option

  • Save mpilone/6156009 to your computer and use it in GitHub Desktop.

Select an option

Save mpilone/6156009 to your computer and use it in GitHub Desktop.

Revisions

  1. mpilone revised this gist Aug 5, 2013. 1 changed file with 17 additions and 7 deletions.
    24 changes: 17 additions & 7 deletions BackgroundUiTask.java
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,7 @@
    import java.util.concurrent.*;
    import java.util.concurrent.locks.Lock;

    import com.vaadin.server.VaadinSession;
    import com.vaadin.ui.UI;
    import com.vaadin.ui.UIDetachedException;

    @@ -13,7 +14,6 @@
    * {@link #doUiUpdate(Throwable)} method.
    *
    * @author mpilone
    *
    */
    public abstract class BackgroundUiTask implements RunnableFuture<Void> {

    @@ -50,8 +50,9 @@ protected void doUiUpdate() {
    protected abstract void doWork();

    /**
    * A simple runnable that executes the two parts of a
    * {@link BackgroundUiWorker}, aborting if the outer task is cancelled.
    * A simple runnable that executes the first of two parts of a
    * {@link BackgroundUiTask}. The {@link BackgroundUiTask#doWork()} method will
    * be called and the {@link BackgroundUiTask#doneLatch} will be decremented.
    *
    * @author mpilone
    */
    @@ -78,8 +79,10 @@ public void run() {
    }

    /**
    * A simple runnable that executes the two parts of a
    * {@link BackgroundUiWorker}, aborting if the outer task is cancelled.
    * A simple runnable that executes the second of two parts of a
    * {@link BackgroundUiTask}. The
    * {@link BackgroundUiTask#doUiUpdate(Throwable)} method will be called and
    * the {@link BackgroundUiTask#doneLatch} will be decremented.
    *
    * @author mpilone
    */
    @@ -150,6 +153,9 @@ public BackgroundUiTask() {
    public BackgroundUiTask(UI ui) {
    this.ui = ui;

    // We wrap the runnable in a future task to get a common API to which to
    // delegate. The task also handles all the tricky exception handling and
    // thread safe cancellation.
    this.future = new FutureTask<Void>(new DoWorkRunnable(), null);
    }

    @@ -230,23 +236,27 @@ public boolean isDone() {
    @Override
    public void run() {

    // Run the initial future, the doWork runnable.
    ((FutureTask<Void>) future).run();

    synchronized (FUTURE_MUTEX) {
    Throwable exception = null;
    try {
    // Check if we got an exception during execution.
    future.get();
    }
    catch (Throwable ex) {
    exception = ex;
    }

    if (future.isCancelled()) {
    // Cancelled during doWork so we'll skip doUiUpdate.
    // Cancelled during doWork so we'll skip doUiUpdate and simply count
    // down.
    doneLatch.countDown();
    }
    else {
    // Fire up doUiUpdate.
    // Fire up doUiUpdate runnable which gets us the second future of the
    // task.
    future = ui.access(new DoUiUpdateRunnable(exception));
    }
    }
  2. mpilone revised this gist Aug 5, 2013. 1 changed file with 12 additions and 4 deletions.
    16 changes: 12 additions & 4 deletions BackgroundUiTask.java
    Original file line number Diff line number Diff line change
    @@ -111,16 +111,24 @@ public void run() {
    }
    }

    /**
    * The mutex for swapping futures to ensure that the active future is safely
    * accessed during all delegated calls.
    */
    private final Object FUTURE_MUTEX = new Object();

    /**
    * The active future that is running/will be run. When the future is complete,
    * it must count down the {@link #doneLatch}.
    */
    private Future<Void> future;

    private CountDownLatch doneLatch = new CountDownLatch(2);

    /**
    * The logger for this class.
    * The count down latch used to keep track of the number of futures that need
    * to execute before this task is considered complete. Calls to {@link #get()}
    * will block on this latch until it reaches 0.
    */
    // private final Logger log = LoggerFactory.getLogger(getClass());
    private CountDownLatch doneLatch = new CountDownLatch(2);

    /**
    * The UI to synchronize with when updating from a background thread.
  3. mpilone revised this gist Aug 5, 2013. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions BackgroundUiTask.java
    Original file line number Diff line number Diff line change
    @@ -18,8 +18,8 @@
    public abstract class BackgroundUiTask implements RunnableFuture<Void> {

    /**
    * Called when a lock has been obtained on the UI {@link VaadinServiceSession}
    * and it is safe to apply UI updates. This method will only be called after
    * Called when a lock has been obtained on the UI's {@link VaadinSession} and
    * it is safe to apply UI updates. This method will only be called after
    * {@link #doWork()} is complete and the task hasn't been cancelled. The
    * default implementation calls {@link #doUiUpdate()}. This method will be
    * called even if {@link #doWork()} throws an exception to give the task a
  4. mpilone revised this gist Aug 5, 2013. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion BackgroundUiTask.java
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,8 @@
    /**
    * A background task that handles synchronizing to the UI when performing
    * updates from a background thread. The common use case is to create a
    * background task, add it to a thread poo and then update the UI wh
    * background task, add it to a thread pool and then update the UI in the safe,
    * {@link #doUiUpdate(Throwable)} method.
    *
    * @author mpilone
    *
  5. mpilone created this gist Aug 5, 2013.
    245 changes: 245 additions & 0 deletions BackgroundUiTask.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,245 @@
    package org.mpilone.vaadin;

    import java.util.concurrent.*;
    import java.util.concurrent.locks.Lock;

    import com.vaadin.ui.UI;
    import com.vaadin.ui.UIDetachedException;

    /**
    * A background task that handles synchronizing to the UI when performing
    * updates from a background thread. The common use case is to create a
    * background task, add it to a thread poo and then update the UI wh
    *
    * @author mpilone
    *
    */
    public abstract class BackgroundUiTask implements RunnableFuture<Void> {

    /**
    * Called when a lock has been obtained on the UI {@link VaadinServiceSession}
    * and it is safe to apply UI updates. This method will only be called after
    * {@link #doWork()} is complete and the task hasn't been cancelled. The
    * default implementation calls {@link #doUiUpdate()}. This method will be
    * called even if {@link #doWork()} throws an exception to give the task a
    * chance to cleanup the UI.
    *
    * @param ex
    * the exception raised in {@link #doWork()} or null if no exception
    * was raised
    */
    protected void doUiUpdate(Throwable ex) {
    doUiUpdate();
    }

    /**
    * A convenience method that will be called by {@link #doUiUpdate(Throwable)}.
    * The default implementation does nothing but subclasses can override this
    * method if the value of the exception is not important.
    */
    protected void doUiUpdate() {
    }

    /**
    * Called when the task is started. All time consuming work should be done in
    * this method. No UI updates are permitted in this method because it is not
    * synchronized to the {@link VaadinServiceSession}. Good implementations
    * should attempt to stop if the task is cancelled while processing.
    */
    protected abstract void doWork();

    /**
    * A simple runnable that executes the two parts of a
    * {@link BackgroundUiWorker}, aborting if the outer task is cancelled.
    *
    * @author mpilone
    */
    private class DoWorkRunnable implements Runnable {
    /*
    * (non-Javadoc)
    *
    * @see java.lang.Runnable#run()
    */
    @Override
    public void run() {
    try {
    if (ui == null) {
    throw new UIDetachedException("No UI available for "
    + "background synchronization.");
    }

    doWork();
    }
    finally {
    doneLatch.countDown();
    }
    }
    }

    /**
    * A simple runnable that executes the two parts of a
    * {@link BackgroundUiWorker}, aborting if the outer task is cancelled.
    *
    * @author mpilone
    */
    private class DoUiUpdateRunnable implements Runnable {
    private Throwable exception;

    public DoUiUpdateRunnable(Throwable exception) {
    this.exception = exception;
    }

    /*
    * (non-Javadoc)
    *
    * @see java.lang.Runnable#run()
    */
    @Override
    public void run() {
    try {
    if (ui == null) {
    throw new UIDetachedException("No UI available for "
    + "background synchronization.");
    }

    doUiUpdate(exception);
    }
    finally {
    doneLatch.countDown();
    }
    }
    }

    private final Object FUTURE_MUTEX = new Object();

    private Future<Void> future;

    private CountDownLatch doneLatch = new CountDownLatch(2);

    /**
    * The logger for this class.
    */
    // private final Logger log = LoggerFactory.getLogger(getClass());

    /**
    * The UI to synchronize with when updating from a background thread.
    */
    private UI ui;

    /**
    * Constructs the background task which will synchronized with the UI
    * available at {@link UI#getCurrent()}. The task must be initially
    * constructed in the main thread where the UI is available.
    */
    public BackgroundUiTask() {
    this(UI.getCurrent());
    }

    /**
    * Constructs the background task which will synchronized with the given UI.
    */
    public BackgroundUiTask(UI ui) {
    this.ui = ui;

    this.future = new FutureTask<Void>(new DoWorkRunnable(), null);
    }

    /*
    * (non-Javadoc)
    *
    * @see java.util.concurrent.Future#cancel(boolean)
    */
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
    synchronized (FUTURE_MUTEX) {
    return future.cancel(mayInterruptIfRunning);
    }
    }

    /**
    * A convenience method for {@link #cancel(boolean)} with a value of false.
    */
    public void cancel() {
    cancel(false);
    }

    /*
    * (non-Javadoc)
    *
    * @see java.util.concurrent.Future#get()
    */
    @Override
    public Void get() throws InterruptedException, ExecutionException {
    doneLatch.await();
    return future.get();
    }

    /*
    * (non-Javadoc)
    *
    * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)
    */
    @Override
    public Void get(long timeout, TimeUnit unit) throws InterruptedException,
    ExecutionException, TimeoutException {

    if (doneLatch.await(timeout, unit)) {
    return future.get(timeout, unit);
    }
    else {
    throw new TimeoutException();
    }
    }

    /*
    * (non-Javadoc)
    *
    * @see java.util.concurrent.Future#isCancelled()
    */
    @Override
    public boolean isCancelled() {
    synchronized (FUTURE_MUTEX) {
    return future.isCancelled();
    }
    }

    /*
    * (non-Javadoc)
    *
    * @see java.util.concurrent.Future#isDone()
    */
    @Override
    public boolean isDone() {
    return doneLatch.getCount() == 0 && future.isDone();
    }

    /*
    * (non-Javadoc)
    *
    * @see java.util.concurrent.RunnableFuture#run()
    */
    @Override
    public void run() {

    ((FutureTask<Void>) future).run();

    synchronized (FUTURE_MUTEX) {
    Throwable exception = null;
    try {
    future.get();
    }
    catch (Throwable ex) {
    exception = ex;
    }

    if (future.isCancelled()) {
    // Cancelled during doWork so we'll skip doUiUpdate.
    doneLatch.countDown();
    }
    else {
    // Fire up doUiUpdate.
    future = ui.access(new DoUiUpdateRunnable(exception));
    }
    }
    }
    }