All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.signalr4j.client.SignalRFuture Maven / Gradle / Ivy

There is a newer version: 2.0.4
Show newest version
/*
Copyright (c) Microsoft Open Technologies, Inc.
All Rights Reserved
See License.txt in the project root for license information.
*/

package com.github.signalr4j.client;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Represents long running SignalR operations
 */
public class SignalRFuture implements Future {
    boolean mIsCancelled = false;
    boolean mIsDone = false;
    private V mResult = null;
    private List mOnCancelled = new ArrayList();
    private List> mOnDone = new ArrayList>();
    private Object mDoneLock = new Object();
    private List mErrorCallback = new ArrayList();
    private Queue mErrorQueue = new ConcurrentLinkedQueue();
    private Object mErrorLock = new Object();
    private Throwable mLastError = null;

    private Semaphore mResultSemaphore = new Semaphore(0);

    /**
     * Handles the cancellation event
     * 
     * @param onCancelled
     *            The handler
     */
    public void onCancelled(Runnable onCancelled) {
        mOnCancelled.add(onCancelled);
    }

    /**
     * Cancels the operation
     */
    public void cancel() {
        mIsCancelled = true;
        if (mOnCancelled != null) {
            for (Runnable onCancelled : mOnCancelled) {
                onCancelled.run();
            }
        }
        mResultSemaphore.release();
    }

    /**
     * Sets a result to the future and finishes its execution
     * 
     * @param result
     *            The future result
     */
    public void setResult(V result) {
        synchronized (mDoneLock) {
            mResult = result;
            mIsDone = true;

            if (mOnDone.size() > 0) {
                for (Action handler : mOnDone) {
                    try {
                        handler.run(result);
                    } catch (Exception e) {
                        triggerError(e);
                    }
                }
            }
        }

        mResultSemaphore.release();
    }

    /**
     * Indicates if the operation is cancelled
     * 
     * @return True if the operation is cancelled
     */
    public boolean isCancelled() {
        return mIsCancelled;
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        cancel();
        return true;
    }

    @Override
    public V get() throws InterruptedException, ExecutionException {
        try {
            return get(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            throw new ExecutionException(e);
        }
    }

    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        if (mResultSemaphore.tryAcquire(timeout, unit)) {
            if (errorWasTriggered()) {
                throw new ExecutionException(mLastError);
            } else if (isCancelled()) {
                throw new InterruptedException("Operation was cancelled");
            } else {
                return mResult;
            }
        } else {
            throw new TimeoutException();
        }
    }

    @Override
    public boolean isDone() {
        return mIsDone;
    }

    /**
     * Handles the completion of the Future. If the future was already
     * completed, it triggers the handler right away.
     * 
     * @param action
     *            The handler
     */
    public SignalRFuture done(Action action) {
        synchronized (mDoneLock) {
            mOnDone.add(action);

            if (isDone()) {
                try {
                    action.run(get());
                } catch (Exception e) {
                    triggerError(e);
                }
            }
        }

        return this;
    }

    /**
     * Handles error during the execution of the Future. If it's the first time
     * the method is invoked on the object and errors were already triggered,
     * the handler will be called once per error, right away.
     * 
     * @param errorCallback
     *            The handler
     */
    public SignalRFuture onError(ErrorCallback errorCallback) {
        synchronized (mErrorLock) {
            mErrorCallback.add(errorCallback);
            while (!mErrorQueue.isEmpty()) {
                // Only the first error handler will get the queued errors
                if (errorCallback != null) {
                    errorCallback.onError(mErrorQueue.poll());
                }
            }
        }

        return this;
    }

    /**
     * Triggers an error for the Future
     * 
     * @param error
     *            The error
     */
    public void triggerError(Throwable error) {
        synchronized (mErrorLock) {
            mLastError = error;
            mResultSemaphore.release();
            if (mErrorCallback.size() > 0) {
                for (ErrorCallback handler : mErrorCallback) {
                    handler.onError(error);
                }
            } else {
                mErrorQueue.add(error);
            }
        }
    }
    
    /**
     * Indicates if an error was triggered
     * 
     * @return True if an error was triggered
     */
    public boolean errorWasTriggered() {
        return mLastError != null;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy