playframework/Play20 — pull request #1053 by jroper (2013-04-23 15:20:11+00:00)
Title: Results refactoring

This is a first attempt at results refactoring.

Before looking at anything, notice in the list of files changed, none of the sample apps have changes, yet they all compile and their tests pass - I've managed to achieve a very high level of source backwards compatibility. Also note that we are doing this in a phased approach, trying to deprecate rather than break in the first release. This balancing of breakages verses having a nice API has driven a lot of my decisions.

The major architectural change is the signature to `EssentialAction`, it is now:

```scala
RequestHeader => Iteratee[Array[Byte], SimpleResult]
```

In general, the low level APIs now do not use `Result`, this applies to both Java and Scala. High level APIs, ie action methods, and also Java test helpers, still use `Result`, for backwards compatibility.

Scala
-----

`Result`, `PlainResult`, `AsyncResult` and `ChunkedResult` have all been deprecated.

`ActionBuilder.apply` methods still take functions that return `Result`. These get unwrapped into `Future[SimpleResult]`, which are passed to the body parsers `Iteratee.mapM`. So ordinary actions should be 100% backwards compatible. `ActionBuilder.async` has been introduced for returning `Future[SimpleResult]` in future.

`ChunkedResult` now extends `SimpleResult`. As for handling streaming, if the result has a content length, no streaming is attempted. Otherwise, a streaming strategy is used, this is a new field on `SimpleResult`. The three streaming strategies are:

* Buffer. This is the default. It will buffer the response up to a given max length (default 100kb, configurable both globally and per result), and send the buffered response with a content length. If the buffer exceeds the max length, it falls back to either chunked or simple streaming (close the connection at the end), depending on whether HTTP 1.1 or 1.0 is being used.
* Chunked. This sends the response chunked. It includes an optional iteratee that produces a Map[String, String], these will be sent as trailers in the last chunk. If this iteratee is supplied, it is zipped with the Netty iteratee. Another new feature is that Play will now refuse to send a chunked response to a HTTP 1.0 client, failing with a 505 protocol not supported error code.
* Simple. This sends the response as is, with no content length, and closes the connection once the body is finished. This is used for HTTP 1.0 clients, and also for server sent events.

Test helper methods that return results now return Future[SimpleResult] instead of Result. Test helper methods that extract values from the results now accept Future[SimpleResult] instead of Result. This means as long as you use type inference for the types of these results, source compatibility should generally be maintained in tests.

Slightly unrelated to the rest of the things, I've modified `ActionBuilder` to allow it to be more versatile. It's now parameterised with `R[_]`, which represents the request object, and this allows it to be used to build actions that, for example, define a custom request object that contains the current username. It also allows you to define a custom execution context that should be used to execute the action.

I modified a number of places that used to return `Result` to now return `SimpleResult`, for example, the error handlers in Global. I could have made these return `Future[Result]`, but I'm not sure if there's a big use case for this, and keeping it as `SimpleResult` generally maintains source compatibility.

So typically, a Scala application will have source breakages in the following areas:

* Doing anything with EssentialAction
* Filters
* Some action composition (but not all)
* Some custom body parsers
* Explicitly typing results in Global
* Explicitly typing, or working with, the return values of Helpers.route and friends.

Java
----

Java actions can now return either `Result` or `Future[Result]`. The reason I didn't make either of these `SimpleResult` is to minimise disruption, since Java methods must have an explicit return type, it's not inferred like in Scala, and we eventually want to replace `Result` with `SimpleResult`. `AsyncResult` is deprecated, and is unwrapped very early.

I created a new `SimpleResult` type for Java, this is a Java `Result` that is backed by a scala `SimpleResult`. Most of the lower level APIs (ie not writing actions) use this.

A major breaking change in the Java API is that `Action` (as in for action composition) now works with `Future[SimpleResult]` instead of `Future`. Also, all `Global` methods that used to return `Result` now return `SimpleResult`, and unlike with Scala, we can't rely on type inference to hide these breakages.

In the test APIs, backwards compatibility I think has been 100% retained. The big difference is that now all the `route` methods don't return until the result is available.

Something that I did along the way that isn't really related is JPA transactions are now associated with an HTTP context if one is available, rather than using its own thread local, and there is a `JPA.withTransactionAsync`, which is now used by default by the `@Transactional` action. This means `@Transactional` can now be used in async actions, for example, in an action that makes a WS call and talks to the database when the WS call returns. `withTransactionAsync` registers `onRedeem` and `onError` callbacks with the returned promise to clean up the transaction/connection.