§Java Migration Guide
In order to better fit in to the Java 8 ecosystem, and to allow Play Java users to make more idiomatic use of Java in their applications, Play has switched to using a number of Java 8 types such as CompletionStage
and Function
. Play also has new Java APIs for EssentialAction
, EssentialFilter
, Router
, BodyParser
, and HttpRequestHandler
.
§New Java APIs
There are several API changes to accommodate writing filters and HTTP request handlers in Java, in particular with the HttpRequestHandler
interface. If you are using Scala for these components you are still free to use the Scala API if you wish.
§Filter API
You will most likely use EssentialAction
when creating a filter. You can either use the Filter
API or the lower-level EssentialFilter
API that operates on EssentialAction
s.
§HttpRequestHandler and ActionCreator
The HttpRequestHandler
actually existed in Play 2.4, but now it serves a different purpose. The createAction
and wrapAction
methods have been moved to a new interface called ActionCreator
, and are deprecated in HttpRequestHandler
. These methods are only applied to Java actions, and are used to intercept requests to the controller’s method call, but not all requests.
In 2.5, HttpRequestHandler
’s main purpose is to provide a handler for the request right after it comes in. This is now consistent with what the Scala implementation does, and provides a way for Java users to intercept the handling of all HTTP requests. Normally, the HttpRequestHandler
will call the router to find an action for the request, so the new API allows you to intercept that request in Java before it goes to the router.
§Replaced functional types with Java 8 functional types
A big change in Play 2.5 is the change to use standard Java 8 classes where possible. All functional types have been replaced with their Java 8 counterparts, for example F.Function1<A,R>
has been replaced with java.util.function.Function<A,R>
.
The move to Java 8 types should enable better integration with other Java libraries as well as with built-in functionality in Java 8.
§How to migrate
Step 1: Change all code that references Play functional interfaces to reference Java 8 interfaces instead.
You need to change code that explicitly mentions a type like F.Function1
. For example:
void myMethod(F.Callback0 block) { ... }
becomes
void myMethod(Runnable block) { ... }
The table below shows all the changes:
old interface | new interface |
---|---|
F.Callback0 |
java.lang.Runnable |
F.Callback<A> |
java.util.function.Consumer<A> |
F.Callback2<A,B> |
java.util.function.BiConsumer<A,B> |
F.Callback3<A,B,C> |
No counterpart in Java 8, consider using akka.japi.function.Function3 |
F.Predicate<A> |
java.util.function.Predicate<A> |
F.Function0<A> |
java.util.function.Supplier<A> |
F.Function1<A,R> |
java.util.function.Function<A,R> |
F.Function2<A,B,R> |
java.util.function.BiFunction<A,B,R> |
Step 2: Fix any errors caused by checked exceptions that are thrown inside your lambdas.
Unlike the Play functional interfaces, the Java 8 functional interfaces don’t permit checked exceptions to be thrown. If your lambda expressions throw a checked exception then you’ll need to change the code. (If you don’t throw checked exceptions then you can leave the code unchanged.)
You may get a lot of compiler errors but it’s pretty easy to get your code working again. Let’s suppose your Play 2.4 code uses a F.Callback0
lambda to stop a database:
onClose(() -> {
database.stop(); // <-- can throw an IOException
})
In Play 2.5 the onClose
method has been changed to take a java.lang.Runnable
argument instead of F.Callback0
. Because Runnable
s can’t throw checked exceptions the code above won’t compile in Play 2.5.
To get the code to compile you can change your lambda code to catch the checked exception (IOException
) and wrap it in an unchecked exception (RuntimeException
). It’s OK for a Runnable
to throw an unchecked exception so the code will now compile.
onClose(() -> {
try {
database.stop(); // <-- can throw an IOException
} catch (IOException e) {
throw new RuntimeException(e);
}
})
If you don’t like adding try-catch blocks into your code you can use the Durian library’s Errors
class to handle exceptions for you.
For example, you can get the same behavior as above, where you convert a checked exception into an unchecked exception, with the code below:
onClose(Errors.rethrow().wrap(database::stop));
Durian provides other behaviors too, such as logging an exception or writing your own exception handler. If you want to use Durian you can either include it as a dependency in your project or by copy the source from two classes into your project.
§Replaced F.Promise
with Java 8’s CompletionStage
APIs that use F.Promise
now use the standard Java 8 CompletionStage
class.
§How to migrate
Step 1: Change all code that returns F.Promise
to return CompletionStage
instead. To aid with migration, F.Promise
also implements the CompletionStage
interface, which means any existing code that returns Promise
can still be invoked from code that has been migrated to use CompletionStage
.
Step 2: Replace relevant static methods in F.Promise
with an equivalent method (many of these use the play.libs.concurrent.Futures
helpers, or the statics on CompletableFuture
):
F.Promise method |
alternative |
---|---|
Promise.wrap |
scala.compat.java8.FutureConverters.toJava |
Promise.sequence |
Futures.sequence |
Promise.timeout |
Futures.timeout |
Promise.pure |
CompletableFuture.completedFuture |
Promise.throwing |
Construct CompletableFuture and use completeExceptionally |
Promise.promise |
CompletableFuture.supplyAsync |
Promise.delayed |
Futures.delayed |
Step 3: Replace existing instance methods with their equivalent on CompletionStage
:
F.Promise |
CompletionStage |
---|---|
or |
applyToEither |
onRedeem |
thenAcceptAsync |
map |
thenApplyAsync |
transform |
handleAsync |
zip |
thenCombine (and manually construct a tuple) |
fallbackTo |
handleAsync followed by thenCompose(Function.identity()) |
recover |
exceptionally (or handleAsync with HttpExecution#defaultContext() if you want Http.Context captured). |
recoverWith |
same as recover , then use .thenCompose(Function.identity()) |
onFailure |
whenCompleteAsync (use HttpExecution#defaultContext() if needed) |
flatMap |
thenComposeAsync (use HttpExecution#defaultContext() if needed) |
filter |
thenApplyAsync and implement the filter manually (use HttpExecution#defaultContext() if needed) |
These migrations are explained in more detail in the Javadoc for F.Promise
.
§Replaced F.Option
with Java 8’s Optional
The Play Java API has been converted to use the Java 8 Optional
class instead of Play’s F.Option
type. The F.Option
type has been removed.
§How to migrate
Replace code that uses F.Option
with Optional
. The two types are similar, but their API is different, so you will need to update your code. The main difference between the two types is that while F.Option
inherits java.util.Collection
, Optional
doesn’t.
Here follows a short table that should ease the migration:
F.Option |
Optional |
---|---|
F.Option.None() |
Optional.empty() |
F.Option.Some(v) |
Optional.ofNullable(v) |
o.isDefined() |
o.isPresent() |
o.isEmpty() |
!o.isPresent() |
o.get() |
o.get() |
o.getOrElse(f) |
o.orElseGet(f) or o.orElse(v) |
o.map(f) |
o.map(f) |
Optional
has a lot more combinators, so we highly encourage you to learn its API if you are not familiar with it already.
§Deprecated static APIs
Several static APIs were deprecated in Play 2.5, in favour of using dependency injected components. Using static global state is bad for testability and modularity, and it is recommended that you move to dependency injection for accessing these APIs. You should refer to the list in the Play 2.4 Migration Guide to find the equivalent dependency injected component for your static API.
Next: Crypto Migration Guide