Documentation

You are viewing the documentation for the 2.2.0 release in the 2.2.x series of releases. The latest stable release series is 3.0.x.

§Play 2.1 migration guide

To migrate a Play 2.0.x application to Play 2.1.0 first update Play’s sbt-plugin in the project/plugins.sbt file:

addSbtPlugin("play" % "sbt-plugin" % "2.1.0")

Now update the project/Build.scala file to use the new play.Project class instead of the PlayProject class:

First the import:

import play.Project._

Then the main project creation:

val main = play.Project(appName, appVersion, appDependencies).settings(

Lastly, update your project/build.properties file:

sbt.version=0.12.2

Then clean and re-compile your project using the play command in the Play 2.1.0 distribution:

play clean
play ~run

If any compilation errors cropped up, this document will help you figure out what deprecations or incompatible changes may have caused the errors.

§Changes to the build file

Because Play 2.1 introduces further modularization, you now have to explicitly specify the dependencies your application needs. By default any play.Project will only contain a dependency to the core Play library. You have to select the exact set of optional dependencies your application need. Here are the new modularized dependencies in Play 2.1:

Here is a typical Build.scala file for Play 2.1:

import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

    val appName         = "app-name"
    val appVersion      = "1.0"

    val appDependencies = Seq(
       javaCore, javaJdbc, javaEbean
    )

    val main = play.Project(appName, appVersion, appDependencies).settings(
      // Add your own project settings here      
    )

}

The mainLang parameter for the project is not required anymore. The main language is determined based on the dependencies added to the project. If dependencies contains javaCore then the language is set to JAVA otherwise SCALA.Notice the modularized dependencies in the appDependencies section.

§play.mvc.Controller.form() renamed to play.data.Form.form()

Also related to modularization, the play.data package and its dependencies were moved out from play core to javaCore artifact. As a consequence of this, play.mvc.Controller#form was moved to play.data.Form#form

§play.db.ebean.Model.Finder.join() renamed to fetch()

As part of the cleanup the Finder API join methods are replaced with fetch methods. They behave exactly same.

§Play’s Promise to become Scala’s Future

With the introduction of scala.concurrent.Future in Scala 2.10 the scala ecosystem made a huge jump to unify the various Future and Promise libraries out there.

The fact that Play is now using scala.concurrent.Future directly means that users can effortlessly combine futures/promises coming from both internal API-s or external libraries.

Java users will continue to use a Play’s wrapper around scala.concurrent.Future for now.

Consider the following snippet:

import play.api.libs.iteratee._
import play.api.libs.concurrent._
import akka.util.duration._

def stream = Action {
    AsyncResult {
      implicit val timeout = Timeout(5.seconds)
      val akkaFuture =  (ChatRoomActor.ref ? (Join()) ).mapTo[Enumerator[String]]
      //convert to play promise before sending the response
      akkaFuture.asPromise.map { chunks =>
        Ok.stream(chunks &> Comet( callback = "parent.message"))
      }
    }
  }
  

Using the new scala.concurrent.Future this will become:

import play.api.libs.iteratee._
import play.api.libs.concurrent._
import play.api.libs.concurrent.Execution.Implicits._

import scala.concurrent.duration._

  def stream = Action {
    AsyncResult {
      implicit val timeout = Timeout(5.seconds)
      val scalaFuture = (ChatRoomActor.ref ? (Join()) ).mapTo[Enumerator[String]]
      scalaFuture.map { chunks =>
        Ok.stream(chunks &> Comet( callback = "parent.message"))
      }
    }
  }

Notice the extra imports for:

Generally speaking, if you see error message “error: could not find implicit value for parameter executor”, you probably need to add:

import play.api.libs.concurrent.Execution.Implicits._

(Please see the Scala documentation about Execution context for more information)

And remember that:

§Changes to the Scala JSON API

Play 2.1 comes with a shiny new Scala JSON validator and path navigator. This new API however breaks compatibility with existing JSON parsers.

The play.api.libs.json.Reads type signature has changed. Consider:

trait play.api.libs.json.Reads[A] {
  self =>

  def reads(jsValue: JsValue): A

}

In 2.1 this becomes:

trait play.api.libs.json.Reads[A] {
  self =>

  def reads(jsValue: JsValue): JsResult[A]

}

So, in Play 2.0 an implementation for a JSON serializer for the User type was:

implicit object UserFormat extends Format[User] {

  def writes(o: User): JsValue = JsObject(
    List("id" -> JsNumber(o.id),
      "name" -> JsString(o.name),
      "favThings" -> JsArray(o.favThings.map(JsString(_)))
    )
  )

  def reads(json: JsValue): User = User(
    (json \ "id").as[Long],
    (json \ "name").as[String],
    (json \ "favThings").as[List[String]]
  )

}

In Play 2.1 you will need to refactor it as:

implicit object UserFormat extends Format[User] {

  def writes(o: User): JsValue = JsObject(
    List("id" -> JsNumber(o.id),
      "name" -> JsString(o.name),
      "favThings" -> JsArray(o.favThings.map(JsString(_)))
    )   
  )   

  def reads(json: JsValue): JsResult[User] = JsSuccess(User(
    (json \ "id").as[Long],
    (json \ "name").as[String],
    (json \ "favThings").as[List[String]]
  ))  

}

The API to generate JSON also evolved. Consider:

val jsonObject = Json.toJson(
  Map(
    "users" -> Seq(
      toJson(
        Map(
          "name" -> toJson("Bob"),
          "age" -> toJson(31),
          "email" -> toJson("[email protected]")
        )
      ),
      toJson(
        Map(
          "name" -> toJson("Kiki"),
          "age" -> toJson(25),
          "email" -> JsNull
        )
      )
    )
  )
)

With Play 2.1 this becomes:

val jsonObject = Json.obj(
  "users" -> Json.arr(
    Json.obj(
      "name" -> "Bob",
      "age" -> 31,
      "email" -> "[email protected]"
    ),
    Json.obj(
      "name" -> "Kiki",
      "age" -> 25,
      "email" -> JsNull
    )
  )
)

More information about these features can be found at the Json documentation.

Due to a change in JBoss Netty, cookies are made to be transient by setting their maxAge to be null or None (depending of the API) instead of setting the maxAge to -1. Any value equal to 0 or less for maxAge will cause the cookie to be expired immediately.

The discardingCookies(String\*) (Scala) and discardCookies(String...) (Java) methods on SimpleResult have been deprecated, since these methods are unable to handle cookies set on a particular path, domain or set to be secure. Please use the discardingCookies(DiscardingCookie*) (Scala) and discardCookie (Java) methods instead.

§RequireJS

In Play 2.0 the default behavior for Javascript was to use Google’s Closure CommonJS module support. In Play 2.1 this was changed to use RequireJS instead.

What this means in practice is that by default Play will only minify and combine files in stage, dist, start modes only. In dev mode Play will resolve dependencies client side.

If you wish to use this feature, you will need to add your modules to the settings block of your project/Build.scala file:

requireJs := "main.js"

More information about this feature can be found on the RequireJS documentation page.