Skip to content

Commit

Permalink
Merge pull request #16 from alanw/master
Browse files Browse the repository at this point in the history
Add lambda friendly addCallback.
  • Loading branch information
spkrka committed Oct 30, 2015
2 parents 6ed3354 + 58a5b51 commit a90098d
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 28 deletions.
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,23 @@ final ListenableFuture<A> firstSuccessful = FuturesExtra.select(futures);
#### Success/Failure callbacks
If you are only interested in either successful or failed results of a future,
you can use these callbacks:
You can attach callbacks that are run depending on the results of a future:
```java
final ListenableFuture<A> future = getFuture();
FuturesExtra.addSuccessCallback(future, a -> System.out.println(a));
FuturesExtra.addCallback(future, System.out::println, Throwable::printStackTrace);
```
Alternatively, if you are only interested in either successful or failed
results of a future, you can use:
```java
final ListenableFuture<A> future = getFuture();
FuturesExtra.addSuccessCallback(future, System.out::println);
```
```java
final ListenableFuture<B> future = getFuture();
FuturesExtra.addFailureCallback(future, e -> e.printStackTrace());
FuturesExtra.addFailureCallback(future, System.out::println);
```
#### Completed futures
Expand Down
61 changes: 37 additions & 24 deletions src/main/java/com/spotify/futures/FuturesExtra.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,47 +172,60 @@ public interface Consumer<T> {
}

/**
* A lambda-friendly way to attach a callback to a {@link ListenableFuture}. The callback will
* only be run if the future completes successfully.
* @param future a ListenableFuture to attach the callback to.
* @param consumer a consumer, to be called with the result of the successful future.
* A lambda-friendly way to attach callbacks to a {@link ListenableFuture}. The success
* callback will only be run if the future completes successfully, and failure will
* only be run if the future fails.
* @param future a ListenableFuture to attach the callbacks to.
* @param success a consumer, to be called with the result of the successful future.
* @param failure a consumer, to be called with the result of the failed future.
* @throws NullPointerException if the {@param success} and {@param failure} are null
*/
public static <T> void addSuccessCallback(
ListenableFuture<T> future,
final Consumer<? super T> consumer) {
public static <T> void addCallback(
final ListenableFuture<T> future,
final Consumer<? super T> success,
final Consumer<Throwable> failure) {
if (success == null && failure == null) {
throw new NullPointerException();
}
Futures.addCallback(future, new FutureCallback<T>() {
@Override
public void onSuccess(T result) {
consumer.accept(result);
public void onSuccess(final T result) {
if (success != null) {
success.accept(result);
}
}

@Override
public void onFailure(Throwable t) {
// ignore
public void onFailure(final Throwable throwable) {
if (failure != null) {
failure.accept(throwable);
}
}
});
}

/**
* A lambda-friendly way to attach a callback to a {@link ListenableFuture}. The callback will
* only be run if the future completes successfully.
* @param future a ListenableFuture to attach the callback to.
* @param consumer a consumer, to be called with the result of the successful future.
*/
public static <T> void addSuccessCallback(
ListenableFuture<T> future,
final Consumer<? super T> consumer) {
addCallback(future, consumer, null);
}

/**
* A lambda-friendly way to attach a callback to a {@link ListenableFuture}. The callback will
* only be run if the future fails.
* @param future a ListenableFuture to attach the callback to.
* @param consumer a consumer, to be called with the result of the failed future.
*/
public static <T> void addFailureCallback(
ListenableFuture<T> future,
final Consumer<Throwable> consumer) {
Futures.addCallback(future, new FutureCallback<T>() {
@Override
public void onSuccess(T result) {
// ignore
}

@Override
public void onFailure(Throwable t) {
consumer.accept(t);
}
});
ListenableFuture<T> future,
final Consumer<Throwable> consumer) {
addCallback(future, null, consumer);
}

/**
Expand Down
56 changes: 56 additions & 0 deletions src/test/java/com/spotify/futures/FuturesExtraTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,62 @@ public void validate(Integer value) throws Exception {
}
}

@Test
public void testCallbackForSuccess() throws Exception {
final SettableFuture<Integer> future = SettableFuture.create();
final Consumer<Integer> success = mock(Consumer.class);
final Consumer<Throwable> failure = mock(Consumer.class);

FuturesExtra.addCallback(future, success, failure);

future.set(10);
verify(success).accept(10);
verify(failure, never()).accept(any(Throwable.class));
}

@Test
public void testCallbackForSuccessNullFailure() throws Exception {
final SettableFuture<Integer> future = SettableFuture.create();
final Consumer<Integer> success = mock(Consumer.class);

FuturesExtra.addCallback(future, success, null);

future.set(10);
verify(success).accept(10);
}

@Test
public void testCallbackForFailure() throws Exception {
final SettableFuture<Integer> future = SettableFuture.create();
final Consumer<Integer> success = mock(Consumer.class);
final Consumer<Throwable> failure = mock(Consumer.class);

FuturesExtra.addCallback(future, success, failure);

final Throwable expected = new RuntimeException("boom");
future.setException(expected);
verify(failure).accept(expected);
verify(success, never()).accept(anyInt());
}

@Test
public void testCallbackForFailureNullSuccess() throws Exception {
final SettableFuture<Integer> future = SettableFuture.create();
final Consumer<Throwable> failure = mock(Consumer.class);

FuturesExtra.addCallback(future, null, failure);

final Throwable expected = new RuntimeException("boom");
future.setException(expected);
verify(failure).accept(expected);
}

@Test(expected = NullPointerException.class)
public void testCallbackWithNulls() throws Exception {
final SettableFuture<Integer> future = SettableFuture.create();
FuturesExtra.addCallback(future, null, null);
}

@Test
public void testSuccessCallback() throws Exception {
final SettableFuture<Integer> future = SettableFuture.create();
Expand Down

0 comments on commit a90098d

Please sign in to comment.