diff --git a/Bolts/src/main/java/bolts/Task.java b/Bolts/src/main/java/bolts/Task.java index 8906774..b8e5a42 100644 --- a/Bolts/src/main/java/bolts/Task.java +++ b/Bolts/src/main/java/bolts/Task.java @@ -54,18 +54,15 @@ public class Task { private Exception error; private List> continuations; - private Task() { - continuations = new ArrayList>(); + /* package */ Task() { + continuations = new ArrayList<>(); } /** - * Creates a TaskCompletionSource that orchestrates a Task. This allows the creator of a task to - * be solely responsible for its completion. - * - * @return A new TaskCompletionSource. + * @deprecated Please use {@link bolts.TaskCompletionSource()} instead. */ public static Task.TaskCompletionSource create() { - Task task = new Task(); + Task task = new Task<>(); return task.new TaskCompletionSource(); } @@ -129,8 +126,9 @@ public void waitForCompletion() throws InterruptedException { /** * Creates a completed task with the given value. */ + @SuppressWarnings("unchecked") public static Task forResult(TResult value) { - Task.TaskCompletionSource tcs = Task.create(); + bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); tcs.setResult(value); return tcs.getTask(); } @@ -139,7 +137,7 @@ public static Task forResult(TResult value) { * Creates a faulted task with the given error. */ public static Task forError(Exception error) { - Task.TaskCompletionSource tcs = Task.create(); + bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); tcs.setError(error); return tcs.getTask(); } @@ -147,8 +145,9 @@ public static Task forError(Exception error) { /** * Creates a cancelled task. */ + @SuppressWarnings("unchecked") public static Task cancelled() { - Task.TaskCompletionSource tcs = Task.create(); + bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); tcs.setCancelled(); return tcs.getTask(); } @@ -184,7 +183,7 @@ public static Task delay(long delay, CancellationToken cancellationToken) return Task.forResult(null); } - final Task.TaskCompletionSource tcs = Task.create(); + final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); final ScheduledFuture scheduled = executor.schedule(new Runnable() { @Override public void run() { @@ -265,7 +264,7 @@ public static Task call(final Callable callable, Exe */ public static Task call(final Callable callable, Executor executor, final CancellationToken ct) { - final Task.TaskCompletionSource tcs = Task.create(); + final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); executor.execute(new Runnable() { @Override public void run() { @@ -320,7 +319,7 @@ public static Task> whenAnyResult(Collection>.TaskCompletionSource firstCompleted = Task.create(); + final bolts.TaskCompletionSource> firstCompleted = new bolts.TaskCompletionSource<>(); final AtomicBoolean isAnyTaskComplete = new AtomicBoolean(false); for (Task task : tasks) { @@ -355,7 +354,7 @@ public static Task> whenAny(Collection> tasks) { return Task.forResult(null); } - final Task>.TaskCompletionSource firstCompleted = Task.create(); + final bolts.TaskCompletionSource> firstCompleted = new bolts.TaskCompletionSource<>(); final AtomicBoolean isAnyTaskComplete = new AtomicBoolean(false); for (Task task : tasks) { @@ -439,8 +438,8 @@ public static Task whenAll(Collection> tasks) { return Task.forResult(null); } - final Task.TaskCompletionSource allFinished = Task.create(); - final ArrayList causes = new ArrayList(); + final bolts.TaskCompletionSource allFinished = new bolts.TaskCompletionSource<>(); + final ArrayList causes = new ArrayList<>(); final Object errorLock = new Object(); final AtomicInteger count = new AtomicInteger(tasks.size()); final AtomicBoolean isCancelled = new AtomicBoolean(false); @@ -520,7 +519,7 @@ public Task continueWhile(final Callable predicate, final Continuation> continuation, final Executor executor, final CancellationToken ct) { final Capture>> predicateContinuation = - new Capture>>(); + new Capture<>(); predicateContinuation.set(new Continuation>() { @Override public Task then(Task task) throws Exception { @@ -557,7 +556,7 @@ public Task continueWith( final Continuation continuation, final Executor executor, final CancellationToken ct) { boolean completed; - final Task.TaskCompletionSource tcs = Task.create(); + final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); synchronized (lock) { completed = this.isCompleted(); if (!completed) { @@ -611,7 +610,7 @@ public Task continueWithTask( final Continuation> continuation, final Executor executor, final CancellationToken ct) { boolean completed; - final Task.TaskCompletionSource tcs = Task.create(); + final bolts.TaskCompletionSource tcs = new bolts.TaskCompletionSource<>(); synchronized (lock) { completed = this.isCompleted(); if (!completed) { @@ -769,7 +768,7 @@ public Task onSuccessTask( * scheduled on a different thread). */ private static void completeImmediately( - final Task.TaskCompletionSource tcs, + final bolts.TaskCompletionSource tcs, final Continuation continuation, final Task task, Executor executor, final CancellationToken ct) { executor.execute(new Runnable() { @@ -809,7 +808,7 @@ public void run() { * scheduled on a different thread). */ private static void completeAfterTask( - final Task.TaskCompletionSource tcs, + final bolts.TaskCompletionSource tcs, final Continuation> continuation, final Task task, final Executor executor, final CancellationToken ct) { @@ -870,95 +869,59 @@ private void runContinuations() { } /** - * Allows safe orchestration of a task's completion, preventing the consumer from prematurely - * completing the task. Essentially, it represents the producer side of a Task, providing - * access to the consumer side through the getTask() method while isolating the Task's completion - * mechanisms from the consumer. + * Sets the cancelled flag on the Task if the Task hasn't already been completed. */ - public class TaskCompletionSource { - private TaskCompletionSource() { - } - - /** - * @return the Task associated with this TaskCompletionSource. - */ - public Task getTask() { - return Task.this; - } - - /** - * Sets the cancelled flag on the Task if the Task hasn't already been completed. - */ - public boolean trySetCancelled() { - synchronized (lock) { - if (complete) { - return false; - } - complete = true; - cancelled = true; - lock.notifyAll(); - runContinuations(); - return true; - } - } - - /** - * Sets the result on the Task if the Task hasn't already been completed. - */ - public boolean trySetResult(TResult result) { - synchronized (lock) { - if (complete) { - return false; - } - complete = true; - Task.this.result = result; - lock.notifyAll(); - runContinuations(); - return true; + /* package */ boolean trySetCancelled() { + synchronized (lock) { + if (complete) { + return false; } + complete = true; + cancelled = true; + lock.notifyAll(); + runContinuations(); + return true; } + } - /** - * Sets the error on the Task if the Task hasn't already been completed. - */ - public boolean trySetError(Exception error) { - synchronized (lock) { - if (complete) { - return false; - } - complete = true; - Task.this.error = error; - lock.notifyAll(); - runContinuations(); - return true; + /** + * Sets the result on the Task if the Task hasn't already been completed. + */ + /* package */ boolean trySetResult(TResult result) { + synchronized (lock) { + if (complete) { + return false; } + complete = true; + Task.this.result = result; + lock.notifyAll(); + runContinuations(); + return true; } + } - /** - * Sets the cancelled flag on the task, throwing if the Task has already been completed. - */ - public void setCancelled() { - if (!trySetCancelled()) { - throw new IllegalStateException("Cannot cancel a completed task."); + /** + * Sets the error on the Task if the Task hasn't already been completed. + */ + /* package */ boolean trySetError(Exception error) { + synchronized (lock) { + if (complete) { + return false; } + complete = true; + Task.this.error = error; + lock.notifyAll(); + runContinuations(); + return true; } + } - /** - * Sets the result of the Task, throwing if the Task has already been completed. - */ - public void setResult(TResult result) { - if (!trySetResult(result)) { - throw new IllegalStateException("Cannot set the result of a completed task."); - } - } + /** + * @deprecated Please use {@link bolts.TaskCompletionSource} instead. + */ + public class TaskCompletionSource extends bolts.TaskCompletionSource { - /** - * Sets the error of the Task, throwing if the Task has already been completed. - */ - public void setError(Exception error) { - if (!trySetError(error)) { - throw new IllegalStateException("Cannot set the error on a completed task."); - } + /* package */ TaskCompletionSource() { } } } diff --git a/Bolts/src/main/java/bolts/TaskCompletionSource.java b/Bolts/src/main/java/bolts/TaskCompletionSource.java new file mode 100644 index 0000000..b611b5e --- /dev/null +++ b/Bolts/src/main/java/bolts/TaskCompletionSource.java @@ -0,0 +1,75 @@ +package bolts; + +/** + * Allows safe orchestration of a task's completion, preventing the consumer from prematurely + * completing the task. Essentially, it represents the producer side of a Task, providing + * access to the consumer side through the getTask() method while isolating the Task's completion + * mechanisms from the consumer. + */ +public class TaskCompletionSource { + + private final Task task; + + /** + * Creates a TaskCompletionSource that orchestrates a Task. This allows the creator of a task to + * be solely responsible for its completion. + */ + public TaskCompletionSource() { + task = new Task<>(); + } + + /** + * @return the Task associated with this TaskCompletionSource. + */ + public Task getTask() { + return task; + } + + /** + * Sets the cancelled flag on the Task if the Task hasn't already been completed. + */ + public boolean trySetCancelled() { + return task.trySetCancelled(); + } + + /** + * Sets the result on the Task if the Task hasn't already been completed. + */ + public boolean trySetResult(TResult result) { + return task.trySetResult(result); + } + + /** + * Sets the error on the Task if the Task hasn't already been completed. + */ + public boolean trySetError(Exception error) { + return task.trySetError(error); + } + + /** + * Sets the cancelled flag on the task, throwing if the Task has already been completed. + */ + public void setCancelled() { + if (!trySetCancelled()) { + throw new IllegalStateException("Cannot cancel a completed task."); + } + } + + /** + * Sets the result of the Task, throwing if the Task has already been completed. + */ + public void setResult(TResult result) { + if (!trySetResult(result)) { + throw new IllegalStateException("Cannot set the result of a completed task."); + } + } + + /** + * Sets the error of the Task, throwing if the Task has already been completed. + */ + public void setError(Exception error) { + if (!trySetError(error)) { + throw new IllegalStateException("Cannot set the error on a completed task."); + } + } +} diff --git a/Bolts/src/main/java/bolts/WebViewAppLinkResolver.java b/Bolts/src/main/java/bolts/WebViewAppLinkResolver.java index 6a40576..810e116 100644 --- a/Bolts/src/main/java/bolts/WebViewAppLinkResolver.java +++ b/Bolts/src/main/java/bolts/WebViewAppLinkResolver.java @@ -125,7 +125,7 @@ public Void call() throws Exception { @Override public Task then(Task task) throws Exception { // Load the content in a WebView and use JavaScript to extract the meta tags. - final Task.TaskCompletionSource tcs = Task.create(); + final TaskCompletionSource tcs = new TaskCompletionSource<>(); final WebView webView = new WebView(context); webView.getSettings().setJavaScriptEnabled(true); webView.setNetworkAvailable(false); diff --git a/Bolts/src/test/java/bolts/TaskTest.java b/Bolts/src/test/java/bolts/TaskTest.java index 902afaf..6b09ad2 100644 --- a/Bolts/src/test/java/bolts/TaskTest.java +++ b/Bolts/src/test/java/bolts/TaskTest.java @@ -9,7 +9,9 @@ */ package bolts; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.ArrayList; import java.util.Collection; @@ -25,6 +27,10 @@ import static org.junit.Assert.assertTrue; public class TaskTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + private void runTaskTest(Callable> callable) { try { Task task = callable.call(); @@ -394,7 +400,7 @@ public Void then(Task> task) throws Exception { assertTrue(task.getResult().isCompleted()); assertFalse(task.getResult().isCancelled()); assertFalse(task.getResult().isFaulted()); - assertEquals(10, (int)task.getResult().getResult()); + assertEquals(10, (int) task.getResult().getResult()); return null; } }); @@ -733,7 +739,7 @@ public void testWhenAllCancel() { public Task call() throws Exception { final ArrayList> tasks = new ArrayList<>(); for (int i = 0; i < 20; i++) { - final Task.TaskCompletionSource tcs = Task.create(); + final TaskCompletionSource tcs = new TaskCompletionSource<>(); final int number = i; Task.callInBackground(new Callable() { @@ -958,17 +964,18 @@ public void testContinueWhileAsyncCancellation() { runTaskTest(new Callable>() { public Task call() throws Exception { return Task.forResult(null).continueWhile(new Callable() { - public Boolean call() throws Exception { - return count.get() < 10; - } - }, new Continuation>() { - public Task then(Task task) throws Exception { - if (count.incrementAndGet() == 5) { - cts.cancel(); - } - return null; - } - }, Executors.newCachedThreadPool(), + public Boolean call() throws Exception { + return count.get() < 10; + } + }, new Continuation>() { + public Task then(Task task) + throws Exception { + if (count.incrementAndGet() == 5) { + cts.cancel(); + } + return null; + } + }, Executors.newCachedThreadPool(), cts.getToken()).continueWith(new Continuation() { public Void then(Task task) throws Exception { assertTrue(task.isCancelled()); @@ -980,6 +987,98 @@ public Void then(Task task) throws Exception { }); } + //region TaskCompletionSource + + @Test + public void testTrySetResult() { + TaskCompletionSource tcs = new TaskCompletionSource<>(); + Task task = tcs.getTask(); + assertFalse(task.isCompleted()); + + boolean success = tcs.trySetResult("SHOW ME WHAT YOU GOT"); + assertTrue(success); + assertTrue(task.isCompleted()); + assertEquals("SHOW ME WHAT YOU GOT", task.getResult()); + } + + @Test + public void testTrySetError() { + TaskCompletionSource tcs = new TaskCompletionSource<>(); + Task task = tcs.getTask(); + assertFalse(task.isCompleted()); + + Exception exception = new RuntimeException("DISQUALIFIED"); + boolean success = tcs.trySetError(exception); + assertTrue(success); + assertTrue(task.isCompleted()); + assertEquals(exception, task.getError()); + } + + @Test + public void testTrySetCanceled() { + TaskCompletionSource tcs = new TaskCompletionSource<>(); + Task task = tcs.getTask(); + assertFalse(task.isCompleted()); + + boolean success = tcs.trySetCancelled(); + assertTrue(success); + assertTrue(task.isCompleted()); + assertTrue(task.isCancelled()); + } + + @Test + public void testTrySetOnCompletedTask() { + TaskCompletionSource tcs = new TaskCompletionSource<>(); + tcs.setResult(null); + + assertFalse(tcs.trySetResult(null)); + assertFalse(tcs.trySetError(new RuntimeException())); + assertFalse(tcs.trySetCancelled()); + } + + @Test + public void testSetResultOnCompletedTask() { + TaskCompletionSource tcs = new TaskCompletionSource<>(); + tcs.setResult(null); + + thrown.expect(IllegalStateException.class); + tcs.setResult(null); + } + + @Test + public void testSetErrorOnCompletedTask() { + TaskCompletionSource tcs = new TaskCompletionSource<>(); + tcs.setResult(null); + + thrown.expect(IllegalStateException.class); + tcs.setError(new RuntimeException()); + } + + @Test + public void testSetCancelledOnCompletedTask() { + TaskCompletionSource tcs = new TaskCompletionSource<>(); + tcs.setResult(null); + + thrown.expect(IllegalStateException.class); + tcs.setCancelled(); + } + + //endregion + + //region deprecated + + @SuppressWarnings("deprecation") + @Test + public void testDeprecatedTaskCompletionSource() { + Task.TaskCompletionSource tcsA = Task.create(); + tcsA.setResult(null); + assertTrue(tcsA.getTask().isCompleted()); + + TaskCompletionSource tcsB = Task.create(); + tcsB.setResult(null); + assertTrue(tcsA.getTask().isCompleted()); + } + @SuppressWarnings("deprecation") @Test public void testDeprecatedAggregateExceptionMethods() { @@ -1010,4 +1109,6 @@ public void testDeprecatedAggregateExceptionMethods() { assertNotSame(error2, aggregate.getErrors().get(2)); assertEquals(error2, aggregate.getErrors().get(2).getCause()); } + + //endregion }