Play Framework – Adapting Java 8 CompletableFutures to Play F.Promises
Recently, I have been playing around with Java 8 and the Play Framework (2.4RC1) – bad pun intended :P. As I worked to develop the codebase, I found the need to integrate with Java 8 CompletableFuture
‘s. No big deal, right?
At first, it didn’t seem to be, but as I continued coding, I quickly became very unhappy with how my code was turning out. The Play Framework for Java introduces some classes to handle promises. The Play p.l.F.Promises
and p.l.F.RedeemablePromises
. These classes essentially solve the same problem as j.u.c.Future
, j.u.c.CompletableFuture
. Intermixing such similar constructs within the same code base quickly create a bad code smell. Ultimately, I made a decision… All async code will be written using Java’s j.u.c.Future/CompletableFuture
and not with Play p.l.F.Promise/RedeemablePromise
.
Why? Two reasons:
1. Code reuse.
j.u.c.Future
andj.u.c.CompletableFuture
are part of Java 8 and thus ubiquitousF.Promise
only exists within the Play Framework.- Using pure Java constructs means more flexibility and compatibility with other frameworks.
- Gossip: Play Framework 3.0 for Java may drop
Promises
for JavaFutures
.
2. ReactiveX – Reactive Extensions
- Rx
Observables
work seamlessly withj.u.c.Future
andj.u.c.CompletableFuture
- (Yes, I also plan to use Rx)
Those familiar with Play Java are likely puzzled. As you know, I cannot possible guarantee all async code is written with Java’s j.u.c.Future/CompletableFuture
. Afterall, there are a number of Play Framework APIs that require a p.l.F.Promise
. So what do we do?
Fortunately, the solution is relatively simple. We adapt!
At this point, I must give credit to matiwinnetou. His RxPlay
adapter is the primary inspiration for this solution.
Previously, we noted that Play’s p.l.F.Promises
and p.l.F.RedeemablePromises
, essentially solve the same problem as j.u.c.Future
, j.u.c.CompletableFuture
. The semantics between the two constructs are very similar, thus implementing an adapter can be done very easily and quite efficiently.
Below is the code, RxFuture
that does just that!
It simply takes as input a j.u.c.CompletableFuture
, then constructs a p.l.F.RedeemablePromise
. The Promise
is completed asynchronously, when the Future
completes. As you can see, we are able to do this very efficiently and with very little code — and most importantly, we still able to maintain asynchronicity!
[code language=”Java”]
package com.dimascio.play;
import play.libs.F;
import java.util.concurrent.CompletableFuture;
public class RxFuture<T> {
private final CompletableFuture<T> cf;
public RxFuture(final CompletableFuture<T> cf) {
this.cf = cf;
}
public static <E> F.Promise<E> toPromise(final CompletableFuture<E> obs) {
return new RxFuture(obs).adopt();
}
public F.Promise<T> adopt() {
F.RedeemablePromise<T> rPromise = F.RedeemablePromise.empty();
cf.whenCompleteAsync((res, err) -> {
if (err != null) {
rPromise.failure(err);
} else {
rPromise.success(res);
}
});
return rPromise;
}
}
[/code]
You can find the gist on Github.
Thanks!
What about the other direction, getting a Promise from play and returning a CompletableFuture?