Skip to content

Commit

Permalink
Merge pull request #15 from spotify/krka/completed
Browse files Browse the repository at this point in the history
Add getCompleted/getException methods
  • Loading branch information
spkrka committed May 4, 2015
2 parents d50bd2b + b14989b commit 72417f9
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
21 changes: 21 additions & 0 deletions README.md
Expand Up @@ -170,6 +170,27 @@ final ListenableFuture<B> future = getFuture();
FuturesExtra.addFailureCallback(future, e -> e.printStackTrace());
```
#### Completed futures
In some cases you want to extract the value (or exception) from the future and you know that
the future is completed so it won't be a blocking operation.

You could use these methods for that, but they will also block if the future is not complete which may lead to
hard to find bugs.
```java
T value = future.get();
T value = Futures.getUnchecked(future);
```

Instead you can use these methods which will never block but instead immediately
throw an exception if the future is not completed. This is typically useful in unit tests
(where futures should be immediate) and in general future callbacks/transforms where you know that a
specific future must be completed for this codepath to be triggered.
```java
T value = FuturesExtra.getCompleted(future);
Throwable exc = FuturesExtra.getException(future);
```

#### JDK 8 CompletableFuture <-> ListenableFuture Conversion

* From `ListenableFuture` To JDK 8 `CompletableFuture`
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/com/spotify/futures/FuturesExtra.java
Expand Up @@ -22,10 +22,12 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.Uninterruptibles;

import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
Expand Down Expand Up @@ -624,4 +626,45 @@ public static <I, O> ListenableFuture<O> asyncTransform(
ListenableFuture<I> input, AsyncFunction<? super I, ? extends O> function) {
return Futures.transform(input, function);
}

/**
* check that a future is completed.
* @param future the future.
* @throws IllegalStateException if the future is not completed.
*/
public static void checkCompleted(ListenableFuture<?> future) {
if (!future.isDone()) {
throw new IllegalStateException("future was not completed");
}
}

/**
* Get the value of a completed future.
*
* @param future a completed future.
* @return the value of the future if it has one.
* @throws IllegalStateException if the future is not completed.
* @throws com.google.common.util.concurrent.UncheckedExecutionException if the future has failed
*/
public static <T> T getCompleted(ListenableFuture<T> future) {
checkCompleted(future);
return Futures.getUnchecked(future);
}

/**
* Get the exception of a completed future.
*
* @param future a completed future.
* @return the exception of a future or null if no exception was thrown
* @throws IllegalStateException if the future is not completed.
*/
public static Throwable getException(ListenableFuture<?> future) {
checkCompleted(future);
try {
Uninterruptibles.getUninterruptibly(future);
return null;
} catch (ExecutionException e) {
return e.getCause();
}
}
}
43 changes: 43 additions & 0 deletions src/test/java/com/spotify/futures/FuturesExtraTest.java
Expand Up @@ -21,6 +21,7 @@
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.spotify.futures.FuturesExtra.Consumer;
import org.junit.Test;

Expand Down Expand Up @@ -571,4 +572,46 @@ public ListenableFuture<String> apply(String a, String b, String c, String d, St
});
future.get();
}

@Test
public void testCheckCompleted() throws Exception {
FuturesExtra.checkCompleted(Futures.immediateFuture("hello"));
FuturesExtra.checkCompleted(Futures.immediateFailedFuture(new RuntimeException()));
}

@Test(expected = IllegalStateException.class)
public void testCheckCompletedFails() throws Exception {
FuturesExtra.checkCompleted(SettableFuture.create());
}

@Test
public void testGetCompleted() throws Exception {
assertEquals("hello", FuturesExtra.getCompleted(Futures.immediateFuture("hello")));
}

@Test(expected = UncheckedExecutionException.class)
public void testGetCompletedThrows() throws Exception {
FuturesExtra.getCompleted(Futures.immediateFailedFuture(new ArrayIndexOutOfBoundsException()));
}

@Test(expected = IllegalStateException.class)
public void testGetCompletedNotComplete() throws Exception {
FuturesExtra.getCompleted(SettableFuture.create());
}

@Test
public void testGetException() throws Exception {
assertEquals(null, FuturesExtra.getException(Futures.immediateFuture("hello")));
}

@Test
public void testGetExceptionThrows() throws Exception {
ArrayIndexOutOfBoundsException t = new ArrayIndexOutOfBoundsException();
assertEquals(t, FuturesExtra.getException(Futures.immediateFailedFuture(t)));
}

@Test(expected = IllegalStateException.class)
public void testExceptiondNotComplete() throws Exception {
FuturesExtra.getException(SettableFuture.create());
}
}

0 comments on commit 72417f9

Please sign in to comment.