Documentation

You are viewing the documentation for the 2.7.0-M4 development release. The latest stable release series is 3.0.x.

§What’s new in Play 2.7

This page highlights the new features of Play 2.7. If you want to learn about the changes you need to make when you migrate to Play 2.7, check out the Play 2.7 Migration Guide.

§Scala 2.13 support

Play 2.7 is the first release of Play to have been cross built against Scala 2.12 and 2.13. Many dependencies were updated so that we can have support for both versions.

You can select which version of Scala you would like to use by setting the scalaVersion setting in your build.sbt.

For Scala 2.12:

scalaVersion := "2.12.6"

For Scala 2.13:

scalaVersion := "2.13.0-M3"

Note: Keep in mind Scala 2.13 still does not have a final version.

§Lifecycle managed by Akka’s Coordinated Shutdown

Play 2.6 introduced the usage of Akka’s Coordinated Shutdown but still didn’t use it all across the core framework or exposed it to the end user. Coordinated Shutdown is an Akka Extension with a registry of tasks that can be run in an ordered fashion during the shutdown of the Actor System.

Coordinated Shutdown internally handles Play 2.7 Play’s lifecycle and an instance of CoordinatedShutdown is available for injection. Coordinated Shutdown gives you fine grained phases - organized as a directed acyclic graph (DAG) - where you can register tasks instead of just having a single phase like Play’s application lifecycle. For example, you can add tasks to run before or after server binding, or after all the current requests finishes. Also, you will have better integration with Akka Cluster.

You can find more details on the new section on Coordinated Shutdown on the Play manual, or you can have a look at Akka’s reference docs on Coordinated Shutdown.

§Guice was upgraded to 4.2.1

Guice, the default dependency injection framework used by Play, was upgraded to 4.2.1 (from 4.1.0). Have a look at the 4.2.1 and the 4.2.0 release notes. This new Guice version introduces breaking changes, so make sure you check the Play 2.7 Migration Guide.

§Constraint annotations offered for Play Java are now @Repeatable

All of the constraint annotations defined by play.data.validation.Constraints are now @Repeatable. This change lets you, for example, reuse the same annotation on the same element several times but each time with different groups. For some constraints however it makes sense to let them repeat itself anyway, like @ValidateWith:

@Validate(groups={GroupA.class})
@Validate(groups={GroupB.class})
public class MyForm {

    @ValidateWith(MyValidator.class)
    @ValidateWith(MyOtherValidator.class)
    @Pattern(value="[a-k]", message="Should be a - k")
    @Pattern(value="[c-v]", message="Should be c - v")
    @MinLength(value=4, groups={GroupA.class})
    @MinLength(value=7, groups={GroupB.class})
    private String name;

    //...
}

You can of course also make your own custom constraints @Repeatable as well and Play will automatically recognise that.

§Payloads for Java validate and isValid methods

When using advanced validation features you can now pass a ValidationPayload object, containing useful information sometimes needed for a validation process, to a Java validate or isValid method.
To pass such a payload to a validate method just annotate your form with @ValidateWithPayload (instead of just @Validate) and implement ValidatableWithPayload (instead of just Validatable):

import java.util.Map;
import com.typesafe.config.Config;
import play.data.validation.Constraints.ValidatableWithPayload;
import play.data.validation.Constraints.ValidateWithPayload;
import play.data.validation.Constraints.ValidationPayload;
import play.i18n.Lang;
import play.i18n.Messages;

@ValidateWithPayload
public class SomeForm implements ValidatableWithPayload<String> {

    @Override
    public String validate(ValidationPayload payload) {
        Lang lang = payload.getLang();
        Messages messages = payload.getMessages();
        Map<String, Object> ctxArgs = payload.getArgs();
        Config config = payload.getConfig();
        // ...
    }

}

In case you wrote your own custom class-level constraint, you can also pass a payload to an isValid method by implementing PlayConstraintValidatorWithPayload (instead of just PlayConstraintValidator):

import javax.validation.ConstraintValidatorContext;

import play.data.validation.Constraints.PlayConstraintValidatorWithPayload;
import play.data.validation.Constraints.ValidationPayload;
// ...

public class ValidateWithDBValidator implements PlayConstraintValidatorWithPayload<SomeValidatorAnnotation, SomeValidatableInterface<?>> {

    //...

    @Override
    public boolean isValid(final SomeValidatableInterface<?> value, final ValidationPayload payload, final ConstraintValidatorContext constraintValidatorContext) {
        // You can now pass the payload on to your custom validate(...) method:
        return reportValidationStatus(value.validate(...., payload), constraintValidatorContext);
    }

}

Note: Don’t get confused with ValidationPayload and ConstraintValidatorContext: The former class is provided by Play and is what you use in your day-to-day work when dealing with forms in Play. The latter class is defined by the Bean Validation specification and is used only internally in Play - with one exception: This class emerges when your write your own custom class-level constraints, like in the last example above, where you only need to pass it on to the reportValidationStatus method however anyway.

§Support for Caffeine

Play now offers a CacheApi implementation based on Caffeine. Caffeine is the recommended cache implementation for Play users.

To migrate from EhCache to Caffeine you will have to remove ehcache from your dependencies and replace it with caffeine. To customize the settings from the defaults, you will also need to update the configuration in application.conf as explained in the documentation.

Read the documentation for the Java cache API and Scala cache API to learn more about configuring caching with Play.

§New Content Security Policy Filter

There is a new Content Security Policy filter available that supports CSP nonce and hashes for embedded content.

The previous setting of enabling CSP by default and setting it to default-src 'self' was too strict, and interfered with plugins. The CSP filter is not enabled by default, and the contentSecurityPolicy in the SecurityHeaders filter is now deprecated and set to null by default.

The CSP filter uses Google’s Strict CSP policy by default, which is a nonce based policy. It is recommended to use this as a starting point, and use the included CSPReport body parsers and actions to log CSP violations before enforcing CSP in production.

§HikariCP upgraded

HikariCP was updated to its latest major version. Have a look at the Migration Guide to see what changed.

§Play WS curl filter for Java

Play WS enables you to create play.libs.ws.WSRequestFilter to inspect or enrich the requests made. Play provides a “log as curl” filter, but this was lacking for Java developers. You can now write something like:

ws.url("https://www.playframework.com")
  .setRequestFilter(new AhcCurlRequestLogger())
  .addHeader("My-Header", "Header value")
  .get();

And then the following log will be printed:

curl \
  --verbose \
  --request GET \
  --header 'My-Header: Header Value' \\
  'https://www.playframework.com'

This can be specially useful if you want to reproduce the request in isolation and also change curl parameters to see how it goes.

§Gzip Filter now supports compression level configuration

When using gzip encoding, you can now configure the compression level to use. You can configure it using play.filters.gzip.compressionLevel, for example:

play.filters.gzip.compressionLevel = 9

See more details at GzipEncoding.

Next: Migration Guides