Typesafe's Play team has released version 2.4 "Damiya" of their web framework, almost exactly one year after the previous major release Play 2.3. The release has been named Damiya in memory of Play contributor Kate von Roeder.
By embracing dependency injection, the refactoring towards better modularization that was started in 2.3 has continued in this release. In that spirit, modules like the Anorm data access layer and Ebean have been extracted into separate projects. Also, Play now requires Java 8 and uses Lambdas and Default Methods in Play's Java-API.
Play has already supported dependecy injection in earlier versions, but now it comes out of the box and its use is even encouraged – APIs that provide or use global state will be deprecated in the next release. The documentation defaults to Google's Guice, but other JSR 330 implementations can be used as well. Scala programmers typically prefer to have their dependencies injected and verified at compile time, this is still supported. The long term goal is to get rid of the global application state altogether, but in Play 2.4, the static reference to the currently running application instance is still there. The most obvious benefit of this refactoring is that applications are easier to test, and it also better aligns with Play's aim to be a stateless framework (meaning that it discourages server session state).
Since the initial release of Play 2, the focus has always been on type safety. Play is built with Scala, compiles its view-templates and prevents you from creating dead links in your application by statically checking your application-internal routing. Unfortunately, this has made it difficult to modularize applications when modules wanted to link to each other without introducing cyclical project-dependencies. This can now be done with the new Aggregating Reverse Routers feature, where a common dependency aggregates the routers of all modules.
Play 2.4 also introduced a new Play-Slick module, which builds on the recently released Slick 3, making it even easier to write asynchronous web applications.
InfoQ had the chance to interview Play's tech lead James Roper about the changes in Play 2.4 and their plans for the future.
InfoQ: You've dropped support for Java 7, according to your roadmap, this step was actually planned for the 3.0 release. Are you already making use of Java 8 features in the API, or why the change?
It was interesting the way this came about, originally we had planned to release Play 2.4 with Akka 2.4. When we found out that Akka 2.4 was only supporting Java 8, that meant that we had to drop support for Java 6 and 7 in Play 2.4. So we took advantage of that, and started taking advantage of new Java 8 features, such as default methods, and also provided out of the box support for Java 8 time APIs in our binding/validation libraries. As the Play 2.4 release neared, we found that Akka 2.4 was slipping by several months, so we decided not to wait for it. But I think only supporting Java 8 is for the best – it has allowed us to provide better APIs to Java users with the default methods, and it also means we can more fully embrace reactive and functional programming styles, knowing that our users will be using lambdas.
Also, it's worth pointing out that Java 7 was end of lifed in May.
InfoQ: Akka HTTP is a new experimental back-end and alternative to Netty. What benefits does the Akka backend have compared to Netty?
Right now, in Play 2.4, I'd say not a lot, but with our plans for the future, here is what it will bring:
- Better HTTP modelling. In Play 3, we are planning on building on top of Akka HTTP's approach to HTTP headers, of providing fully parsed, strongly typed, validated headers, rather than raw strings. This has several benefits, including a much more type safe user experinece, and performance improvements, since the headers don't need to be reparsed when they don't change between requests.
- Much closer to the metal IO handling. In Play 3 we are replacing iteratees with reactive streams (or rather, Akka streams), and Akka HTTP itself is built on Akka streams, so this means there'll be less layers between the application and the wire, which we expect will increase performance.
- Interesting distribution possibilities. This is an area for experimentation, but actor systems are very easy to distribute across a cluster, we hope that in Play we can offer things like the ability to transparently route different requests to different nodes
InfoQ: A major change in 2.4 is the full embracement of dependency injection on the road to getting rid of all global state. There's no discussion that it needs to go, so what's the next step?
The next major release of Play, that is, Play 3, will remove the rest of it. Note – there may be a semi major release, Play 2.5, before Play 3, but this isn't going to have any significant breaking changes, there won't be any progress on the global state front, one of the biggest changes in it is that it will add deprecation warnings to APIs that depend on global state for which alternatives exist.
I guess a bigger question is what's left of the global state to remove. There are two major things left. One is that our request header parsing, particular for session and flash cookies, depends on global state, and is used by the
RequestHeader
objects themselves, lazily when the data is accessed. This will be solved by parsing these things up front, as I mentioned earlier. The other one is our action builders, which depend on the default body parser, which depends on configuration such as the maximum length of a request body, and the error handler. This will be a rather large breaking change - it looks like we'll simply make it required to inject the ActionBuilder, so the effect on actions will most likely be a change fromAction { req => ... }
toaction { req => ... }
, as action builder becomes an injected field rather than a static object.
InfoQ: As a Scala developer striving for type safety, I'm a bit overwhelmed by the choices of dependency injection. In which situation would you recommend run- or compile-time injection?
So are we! While the Java community is more or less agreed on the approach to dependency injection (the debates are more around which framework to use), the Scala community is still debating which approach is best. This is why we've been so unopinionated and why we've left the choice so wide open with Play's DI supported.
If you don't have a strong opinion about compile time DI, then I would recommend using runtime DI. It works well, and you will definitely be successful in using it. If however you have a favourite compile time DI approach that you like in Scala, then I would recommend trying that – and most importantly, let us know about your experiences with it in a blog post. Personally, I have a lot of hope for macwire with Play, but it's too early to call what is going to be the best or most popular DI approach used by Scala developers in Play applications.
InfoQ: Let's say we have a Play 2.3 application using Slick 2 and no dependency injection. How would you proceed to upgrade?
Upgrade to 2.4, and get it working with minimal changes first. Then, switch to DI one controller at a time. As you do this, you may find common functionality that should be extracted into services, do this, but only do one service at a time, getting it working before starting the next service. The temptation is to do everything at once – I can tell you from experience, especially if you have a large app, this has a very high chance of ballooning into a massive change that you'll give up on. Your app is working today relying on global state, it will keep working tomorrow relying on global state, there is no need to remove all the global state at once.
As for the Slick 3 upgrade – I'd say start by creating a utility method that does an
Await.result
, and use that to block on futures returned by Slick. Get the code working, then, piece by piece, change the code to be asynchronous. Why the utility method? Because you don't want to accidentally leave some code blocking, so when you're done, delete the utility method. If there's any code you forgot, it now won't compile.
InfoQ: Thank you for the interview!
For a detailed list of changes, see the Play 2.4 highlights page and don't forget to have a look at the migration guide when upgrading your applications. And please tell us how it went in the comments below.