As of Play 2.6.8, Play also offers a PlayService plugin. This is a much more minimal Play configuration oriented towards microservices. It uses the standard Maven layout instead of the traditional Play layout, and does not include twirl templates or the sbt-web functionality. For example:

The biggest under the hood change is that Play no longer relies on global state. You can still access the global application through play.api.Play.current / play.Play.application() in Play 2.6, but it is deprecated. This sets the stage for Play 3.0, where there is no global state at all.

You can disable access to global application entirely by setting the following configuration value:

play.allowGlobalApplication=false

The above setting will cause an exception on any invocation of Play.current.

Requests in Play 2.6 now contain attributes. Attributes allow you to store extra information inside request objects. For example, you can write a filter that sets an attribute in the request and then access the attribute value later from within your actions.

Attributes are stored in a TypedMap that is attached to each request. TypedMaps are immutable maps that store type-safe keys and values. Attributes are indexed by a key and the key’s type indicates the type of the attribute.

The routes file syntax now allows you to add “modifiers” to each route that provide custom behavior. We have implemented one such tag in the CSRF filter, the “nocsrf” tag. By default, the following route will not have the CSRF filter applied.

Please be aware that the HandlerDef request attribute exists only when using a router generated by Play from a routes file.This attribute is not added when the routes are defined in code, for example using the Scala SIRD or Java RoutingDsl. In this case request.attrs.get(HandlerDef) will return None in Scala or null in Java. Keep this in mind when creating filters.

Twirl templates can now be created with a constructor annotation using @this. The constructor annotation means that Twirl templates can be injected into templates directly and can manage their own dependencies, rather than the controller having to manage dependencies not only for itself, but also for the templates it has to render.

As an example, suppose a template has a dependency on a component TemplateRenderingComponent, which is not used by the controller.

First create a file IndexTemplate.scala.html using the @this syntax for the constructor. Note that the constructor must be placed before the @() syntax used for the template’s parameters for the apply method:

By default all generated Scala template classes Twirl creates with the @this syntax within Play will automatically be annotated with @javax.inject.Inject(). If desired you can change this behavior in build.sbt:

// Add one or more annotation(s):
TwirlKeys.constructorAnnotations += "@java.lang.Deprecated()"
// Or completely replace the default one with your own annotation(s):
TwirlKeys.constructorAnnotations := Seq("@com.google.inject.Inject()")

Now define the controller by injecting the template in the constructor:

Play now comes with a default set of enabled filters, defined through configuration. This provides a “secure by default” experience for new Play applications, and tightens security on existing Play applications.

NOTE: If you are migrating from an existing project that does not use CSRF form helpers such as CSRF.formField, then you may see “403 Forbidden” on PUT and POST requests, from the CSRF filter. To check this behavior, please add <logger name="play.filters.csrf" value="TRACE"/> to your logback.xml. Likewise, if you are running a Play application on something other than localhost, you must configure the AllowedHostsFilter to specifically allow the hostname/ip you are connecting from.

If you have the gzip filter enabled you can now also control which responses are and aren’t gzipped based on their content types via application.conf (instead of writing you own Filters class):

play.filters.gzip {
contentType {
# If non empty, then a response will only be compressed if its content type is in this list.
whiteList = [ "text/*", "application/javascript", "application/json" ]
# The black list is only used if the white list is empty.
# Compress all responses except the ones whose content type is in this list.
blackList = []
}
}

Play now uses JSON Web Token (JWT) format for session and flash cookies. This allows for a standardized signed cookie data format, cookie expiration (making replay attacks harder) and more flexibility in signing cookies.

This opens the door for implicit markers to be passed for logging in several statements, which makes adding context to logging much easier without resorting to MDC. For example, using Logstash Logback Encoder and an implicit conversion chain, request information can be encoded into logging statements automatically:

Note that marker contexts are also very useful for “tracer bullet” style logging, where you want to log on a specific request without explicitly changing log levels. For example, you can add a marker only when certain conditions are met:

In the Java API, we have moved to the standard Config object from Lightbend’s Config library instead of play.Configuration. This brings the behavior in line with standard config behavior, as the methods now expect all keys to exist. See the Java config migration guide for migration details.

In the Scala API, we have introduced new methods to the play.api.Configuration class to simplify the API and allow loading of custom types. You can now use an implicit ConfigLoader to load any custom type you want. Like the Config API, the new Configuration#get[T] expects the key to exist by default and returns a value of type T, but there is also a ConfigLoader[Option[T]] that allows null config values. See the Scala configuration docs for more details.

A security marker has been added for security related operations in Play, and failed security checks now log at WARN level, with the security marker set. This ensures that developers always know why a particular request is failing, which is important now that security filters are enabled by default in Play.

The security marker also allows security failures to be triggered or filtered distinct from normal logging. For example, to disable all logging with the SECURITY marker set, add the following lines to the logback.xml file:

Before, if you want to use a custom logging framework, you had to configure it using Scala, even if the you have a Java project. Now it is possible to create custom LoggerConfigurator in both Java and Scala. To create a LoggerConfigurator in Java, you need to implement the given interface, for example, to configure Log4J:

Note: this implementation is fully compatible with Scala version LoggerConfigurator and can even be used in Scala projects if necessary, which means that module creators can provide a Java or Scala implementation of LoggerConfigurator and they will be usable in both Java and Scala projects.

The Java forms functionality has been split out into a separate module. The forms functionality depends on a few Spring modules and the Hibernate validator, so if you are not using forms, you may wish to remove the Java forms module to avoid those unnecessary dependencies.

This module is automatically included by the PlayJava plugin, but can be disabled by using the PlayMinimalJava plugin instead:

Just as in Scala, Play now has components to enable Java Compile Time Dependency Injection. The components were created as interfaces that you should implements and they provide default implementations. There are components for all the types that could be injected when using Runtime Dependency Injection. To create an application using Compile Time Dependency Injection, you just need to provide an implementation of play.ApplicationLoader that uses a custom implementation of play.BuiltInComponents, for example:

The MessagesApi and Lang classes are used for internationalization in Play, and are required to display error messages in forms.

In the past, putting together a form in Play has required multiple steps, and the creation of a Messages instance from a request was not discussed in the context of form handling.

In addition, it was inconvenient to have a Messages instance passed through all template fragments when form handling was required, and Messages implicit support was provided directly through the controller trait. The I18N API has been refined with the addition of a MessagesProvider trait, implicits that are tied directly to requests, and the forms documentation has been improved.

The MessagesActionBuilder has been added. This action builder provides a MessagesRequest, which is a WrappedRequest that extends MessagesProvider, only a single implicit parameter needs to be made available to templates, and you don’t need to extend Controller with I18nSupport. This is also useful because to use CSRF with forms, both a Request (technically a RequestHeader) and a Messages object must be available to the template.

Support for creating MessagesApi instances has been improved. Now, when you want to create a MessagesApi instance, you can create DefaultMessagesApi() or DefaultLangs() with default arguments. If you want to specify test messages from configuration or from another source, you can pass in those values:

This class defines a custom execution context that delegates to an akka.actor.ActorSystem. It is very useful for situations in which the default execution context should not be used, for example if a database or blocking I/O is being used. Detailed information can be found in the ThreadPools page, but Play 2.6.x adds a CustomExecutionContext class that handles the underlying Akka dispatcher lookup.

For thread pool sizing involving JDBC connection pools, you want a fixed thread pool size matching the connection pool, using a thread pool executor. Following the advice in HikariCP’s pool sizing page, you should configure your JDBC connection pool to double the number of physical cores, plus the number of disk spindles.

There are substantial improvements to Play WSClient. Play WSClient is now a wrapper around the standalone play-ws implementation, which can be used outside of Play. In addition, the underlying libraries involved in play-ws have been shaded, so that the Netty implementation used in it does not conflict with Spark, Play or any other library that uses a different version of Netty.

Finally, there is now support for HTTP Caching if a cache implementation is present. Using an HTTP cache means savings on repeated requests to backend REST services, and is especially useful when combined with resiliency features such as stale-on-error and stale-while-revalidate.

Now, tuples are able to be serialized by play-json, and there are Reads and Writes implementations in the implicit scope. Tuples are serialized to arrays, so ("foo", 2, "bar") will render as ["foo", 2, "bar"] in the JSON.

Uploading files uses a TemporaryFile API which relies on storing files in a temporary filesystem, as specified in ScalaFileUpload / JavaFileUpload, accessible through the ref attribute.

Uploading files is an inherently dangerous operation, because unbounded file upload can cause the filesystem to fill up – as such, the idea behind TemporaryFile is that it’s only in scope at completion and should be moved out of the temporary file system as soon as possible. Any temporary files that are not moved are deleted.

In 2.5.x, TemporaryFile were deleted as the file references were garbage collected, using finalize. However, under certain conditions, garbage collection did not occur in a timely fashion. The background cleanup has been moved to use FinalizableReferenceQueue and PhantomReferences rather than use finalize.

The Java and Scala APIs for TemporaryFile has been reworked so that all TemporaryFile references come from a TemporaryFileCreator trait, and the implementation can be swapped out as necessary, and there’s now an atomicMoveWithFallback method that uses StandardCopyOption.ATOMIC_MOVE if available.

The above configuration will delete files that are more than 30 minutes old, using the “olderThan” property. It will start the reaper five minutes after the application starts, and will check the filesystem every 30 seconds thereafter. The reaper is not aware of any existing file uploads, so protracted file uploads may run into the reaper if the system is not carefully configured.