com.github.signalr4j.client.SignalRFuture Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of signalr4j Show documentation
Show all versions of signalr4j Show documentation
A java client library for accessing SignalR WebSocket endpoints.
/*
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;
}
}