Documentation

You are viewing the documentation for the 2.9.x release series. The latest stable release series is 3.0.x.

§Streams Migration Guide

Play 2.5 has made several major changes to how it streams data and response bodies.

  1. Play 2.5 uses Akka Streams for streaming. Previous versions of Play used iteratees for streaming as well as several other ad-hoc types of streaming, such as WebSocket, Chunks, etc.

    There are two main benefits of the change to use Akka Streams. First, Java users can now access the full feature set of Play, e.g. writing body parsers and filters. Second, the streaming library is now more consistent across Play.

  2. Play 2.5 uses ByteString to hold packets of bytes. Previously, Play used byte arrays (byte[]/ArrayByte) to hold bytes.

    The ByteString class is immutable like Java’s String, so it’s safer and easier to use. Like String it does have a small performance cost, because it copies its data when it is constructed, but this is balanced by its cheap concatenation and substring operations.

  3. Play 2.5 has a new HttpEntity type for response bodies. Previously response bodies were a plain stream of bytes. HTTP bodies are now a type of HttpEntity: Strict, Streamed or Chunked.

    By telling Play what type of entity to use, applications can have more control over how Play sends HTTP responses. It also makes it easier for Play to optimize how it delivers the body.

§Summary of changes

The following parts of the Play API have been updated:

The following types have been changed:

Purpose Old types New type
Holding bytes byte[]/Array[Byte] ByteString
Producing a stream Enumerator, WebSocket.Out, Chunks.Out, EventSource.Out Source
Transforming a stream to another stream Enumeratee Flow
Transforming a stream to a single value Iteratee Accumulator
Consuming a stream Iteratee Sink

§How to migrate (by API)

The following section gives an overview of how to migrate code that uses different parts of the API.

§Migrating chunked results (chunked, Results.Chunked)

In Play 2.4 you would create chunked results in Scala with an Enumerator and in Java with a Results.Chunked object. In Play 2.5 these parts of the API are still available, but they have been deprecated.

If you choose to migrate to the new API, you can create a chunked result by calling the chunked method on a StatusHeader object and providing an Akka Streams Source object for the stream of chunks.

More advanced users may prefer to explicitly create an HttpEntity.Chunked object and pass it into the Result object constructor.

§Migrating streamed results (feed, stream) (Scala only)

In Play 2.4 Scala users could stream results by passing an Enumerator to the feed or stream method. (Java users didn’t have a way to stream results, apart from chunked results.) The feed method streamed the Enumerator’s data then closed the connection. The stream method, either streamed or chunked the result and possibly closed the connection, depending on the HTTP version of the connection and the presence or absence of the Content-Length header.

In Play 2.5, the stream method has been removed and the feed method has been deprecated. You can choose whether or not to migrate the feed method to the new API. If you use the stream method your code will need to be changed.

The new API is to create a Result object directly and choose an HttpEntity to represent its body. For streamed results, you can use the HttpEntity.Streamed class. The Streamed class takes a Source as a body and an optional Content-Length header value. The Source’s content will be sent to the client. If the entity has a Content-Length header then the connection will be left open, otherwise it will be closed to signal the end of the stream.

§Migrating WebSockets (WebSocket)

In Play 2.4, a WebSocket’s bidirectional stream was represented in Java with a pair of WebSocket.In and WebSocket.Out objects and in Scala with a pair of Enumerator and Iteratee objects. In Play 2.5, both Java and Scala now use an Akka Streams Flow to represent the bidirectional stream.

To migrate your WebSockets code in Play 2.5 you have two options.

The first option is to use the old Play API, which has been deprecated and renamed to LegacyWebSocket. This is the easiest option. You just need to change your code that refers to WebSocket to refer to LegacyWebSocket instead. The LegacyWebSocket class gives you an easy migration path from Play 2.4 to Play 2.5.

The second option is to change to the new Play API. To do this you’ll need to change your WebSocket code to use an Akka Streams Flow object.

§Migrating Scala WebSockets

The Play 2.4 Scala WebSocket API requires an Enumerator/Iteratee pair that produces In objects and consumes Out objects. A pair of FrameFormatters handle the job of getting the data out of the In and Out objects.

case class WebSocket[In, Out](f: RequestHeader => Future[Either[Result, (Enumerator[In], Iteratee[Out, Unit]) => Unit]])(implicit val inFormatter: WebSocket.FrameFormatter[In], val outFormatter: WebSocket.FrameFormatter[Out]) extends Handler {
trait FrameFormatter[A] {
  def transform[B](fba: B => A, fab: A => B): FrameFormatter[B]
}

The Play 2.5 Scala WebSocket API is built around a Flow of Messages. A Message represents a WebSocket frame. The MessageFlowTransformer type handles transforming high-level objects, like JSON, XML and bytes into Message frames. A set of built-in implicit MessageFlowTransformers are provided, and you can also write your own.

trait WebSocket extends Handler {
  def apply(request: RequestHeader): Future[Either[Result, Flow[Message, Message, _]]]
}
sealed trait Message
case class TextMessage(data: String) extends Message
case class BinaryMessage(data: ByteString) extends Message
case class CloseMessage(statusCode: Option[Int] = Some(CloseCodes.Regular), reason: String = "") extends Message
case class PingMessage(data: ByteString) extends Message
case class PongMessage(data: ByteString) extends Message
trait MessageFlowTransformer[+In, -Out] { self =>
  def transform(flow: Flow[In, Out, _]): Flow[Message, Message, _]
}

To migrate, you’ll need to translate the bidirectional Enumerator/Iteratee stream into a Flow. You may also need to convert your In/Out objects into Messages using a MessageFlowTransformer, although this is not necessary for common types like JSON, since some built-in implicit conversions are provided.

§Migrating Java WebSockets

The Play 2.4 Java WebSocket API uses a WebSocket.In object to handle incoming messages and a WebSocket.Out object to send outgoing messages. The API supported WebSockets transporting text, bytes or JSON frames.

return WebSocket.whenReady((in, out) -> {
    out.write("Hello!");
    out.close();
});

The new Play 2.5 API is much more powerful. You can now create a WebSocket and return arbitrary WebSocket Message frames. The bidirectional Message streams are represented as a Flow.

public abstract class WebSocket {
    public abstract CompletionStage<F.Either<Result, Flow<Message, Message, ?>>> apply(Http.RequestHeader request);
}

If you want to convert WebSocket Message frames to your own types you can use the MappedWebSocketAcceptor class. Several of these classes are provided for you: Text, Binary and Json. For example:

return WebSocket.Text.accept(requestHeader -> {
  // return a Flow<String, String, ?>
})

You can also create your own MappedWebSocketAcceptor by defining how to convert incoming outgoing messages.

§Migrating Comet

To use [Comet](https://en.wikipedia.org/wiki/Comet_(programming)) in Play you need to produce a chunked HTTP response with specially formatted chunks. Play has a Comet class to help produce events on the server that can be sent to the browser. In Play 2.4.x, a new Comet instance had to be created and used callbacks for Java, and an Enumeratee was used for Scala. In Play 2.5, there are new APIs added based on Akka Streams.

§Migrating Java Comet

Create an Akka Streams source for your objects, and convert them into either String or JsonNode objects. From there, you can use play.libs.Comet.string or play.libs.Comet.json to convert your objects into a format suitable for Results.ok().chunked(). There is additional documentation in JavaComet.

Because the Java Comet helper is based around callbacks, it may be easier to turn the callback based class into a org.reactivestreams.Publisher directly and use Source.fromPublisher to create a source.

§Migrating Scala Comet

Create an Akka Streams source for your objects, and convert them into either String or JsValue objects. From there, you can use play.api.libs.Comet.string or play.api.libs.Comet.json to convert your objects into a format suitable for Ok.chunked(). There is additional documentation in ScalaComet.

§Migrating Server-Sent events (EventSource)

To use Server-Sent Events in Play you need to produce a chunked HTTP response with specially formatted chunks. Play has an EventSource interface to help produce events on the server that can be sent to the browser. In Play 2.4 Java and Scala each had quite different APIs, but in Play 2.5 they have been changed so they’re both based on Akka Streams.

§Migrating Java Server-Sent events

In Play 2.4’s Java API you produce your stream of chunks with EventSource, which is a class that extends Chunks<String>. You can construct Event objects from strings or JSON objects and then send them in the response by calling EventSource’s send method.

EventSource eventSource = new EventSource() {
    @Override
    public void onConnected() {
        send(Event.event("hello"));
        send(Event.event("world"));
        ...
    }
};
return ok(eventSource);

In Play 2.5 you’ll typically create an Akka Streams Source for your application objects, use Source.map to convert your objects to Events then finally use EventSource.chunked to convert the Events into chunked values. The example below shows how this works for sending a stream of strings.

Source<String, ?> stringSource = ...;
Source<EventSource.Event, ?> eventSource = myStrings.map(Event::event);
return ok().chunked(EventSource.chunked(eventSource)).as("text/event-stream");

If you still want to use the same API as in Play 2.4 you can use the LegacyEventSource class. This class is the same as the Play 2.4 API, but it has been renamed and deprecated. If you want to use the new API, but retain the same feel as the old imperative API, you can try GraphStage.

§Migrating Scala Server-Sent events

To use Play 2.4’s Scala API you provide an Enumerator of application objects then use the EventSource Enumeratee to convert them into Events. Finally you pass the Events to the chunked method where they’re converted into chunks.

val someDataStream: Enumerator[SomeData] = ???
Ok.chunked(someDataStream &> EventSource())

In Play 2.5 using EventSource with Enumerators and Enumeratees has been deprecated. You can still use an Enumerator and Enumeratee, but it is recommended that you convert your code to use a Source and a Flow instead. The Source produces the stream of objects and EventSource.flow’s Flow converts them into Events. For example, code above would be rewritten as:

val someDataStream: Source[SomeData, Unit] = ???
Ok.chunked(someDataStream via EventSource.flow).as("text/event-stream")

§Migrating custom actions (EssentialAction) (Scala only)

Most Scala users will use the Action class for their actions. The Action class is a type of EssentialAction that always parses its body fully before running its logic and sending a result. Some users may have written their own custom EssentialActions so that they can do things like incrementally processing the request body.

If you’re only using normal Actions in your Play 2.4 application then they do not need any migration. However, if you’ve written an EssentialAction, then you’ll need to migrate it to the new API in Play 2.5. The behavior of an EssentialAction is still the same, but the signature has changed from Play 2.4:

trait EssentialAction extends (RequestHeader => Iteratee[Array[Byte], Result])

to a new signature in Play 2.5:

trait EssentialAction extends (RequestHeader => Accumulator[ByteString, Result])

To migrate, you’ll need to replace your Iteratee with an Accumulator and your Array[Byte] with a ByteString.

§Migrating custom body parsers (BodyParser) (Scala only)

If you’re a Scala user who has a custom BodyParser in their Play 2.4 application then you’ll need to migrate it to the new Play 2.5 API. The BodyParser trait signature looks like this in Play 2.4:

trait BodyParser[+A] extends (RequestHeader => Iteratee[Array[Byte], Either[Result, A]])

In Play 2.5 it has changed to use Akka Streams types:

trait BodyParser[+A] extends (RequestHeader => Accumulator[ByteString, Either[Result, A]])

To migrate, you’ll need to replace your Iteratee with an Accumulator and your Array[Byte] with a ByteString.

§Migrating Result bodies (Scala only)

The Result object has changed how it represents the result body and the connection close flag. Instead of taking body: Enumerator[Array[Byte]], connection: Connection, it now takes body: HttpEntity. The HttpEntity type contains information about the body and implicit information about how to close the connection.

You can migrate your existing Enumerator by using a Streamed entity that contains a Source and an optional Content-Length and Content-Type header.

val bodyPublisher: Publisher[ByteString] = Streams.enumeratorToPublisher(bodyEnumerator)
val bodySource: Source[ByteString, _] = Source.fromPublisher(bodyPublisher)
val entity: HttpEntity = HttpEntity.Streamed(bodySource)
new Result(headers, entity)

See the section on migrating Enumerators and migrating to ByteString for more information on migrating to these types.

You may find that you don’t need a stream for the Result body at all. If that’s the case you might want to use a Strict entity for the body.

new Result(headers, HttpEntity.Strict(bytes))

§How to migrate (by type)

This section explains how to migrate your byte arrays and streams to the new Akka Streams APIs.

Akka Streams is part of the Akka project. Play uses Akka Streams to provide streaming functionality: sending and receiving sequences of bytes and other objects. The Akka project has a lot of good documentation about Akka Streams. Before you start using Akka Streams in Play it is worth looking at the Akka Streams documentation to see what information is available.

The API documentation can be found under the akka.stream package in the main Akka API documentation:

When you’re first getting started with Akka Streams, the Basics and working with Flows section of the Akka documentation is worth a look. It will introduce you to the most important parts of the Akka Streams API.

You don’t need to convert your whole application in one go. Parts of your application can keep using iteratees while other parts use Akka Streams. Akka Streams provides a reactive streams implementation, and Play’s iteratees library also provides a reactive streams implementation, consequently, Play’s iteratees can easily be wrapped in Akka Streams and vice versa.

§Migrating byte arrays (byte[]/Array[Byte]) to ByteStrings

Refer to the Java and Scala API documentation for ByteString.

Examples:

Scala:

// Get the empty ByteString (this instance is cached)
ByteString.empty
// Create a ByteString from a String
ByteString("hello")
ByteString.fromString("hello")
// Create a ByteString from an Array[Byte]
ByteString(arr)
ByteString.fromArray(arr)

Java:

// Get the empty ByteString (this instance is cached)
ByteString.empty();
// Create a ByteString from a String
ByteString.fromString("hello");
// Create a ByteString from an Array[Byte]
ByteString.fromArray(arr);

§Migrating *.Outs to Sources

Play now uses a Source to generate events instead of its old WebSocket.Out, Chunks.Out and EventSource.Out classes. These classes were simple to use, but they were inflexible and they didn’t implement back pressure properly.

You can replace your *.Out class with any Source that produces a stream. There are lots of ways to create Sources (Java/Scala.

If you want to replace your *.Out with a simple object that you can write messages to and then close, without worrying about back pressure, then you can use the Source.actorRef method:

Java:

Source<ByteString, ?> source = Source.<ByteString>actorRef(256, OverflowStrategy.dropNew())
  .mapMaterializedValue(sourceActor -> {
    sourceActor.tell(ByteString.fromString("hello"), null);
    sourceActor.tell(ByteString.fromString("world"), null);
    sourceActor.tell(new Status.Success(NotUsed.getInstance()), null);
    return null;
  });

Scala:

val source = Source.actorRef[ByteString](256, OverflowStrategy.dropNew).mapMaterializedValue { sourceActor =>
  sourceActor ! ByteString("hello")
  sourceActor ! ByteString("world")
  sourceActor ! Status.Success(()) // close the source
}

§Migrating Enumerators to Sources

Play uses Enumerators in many places to produce streams of values.

Step 1: Use transitional API (if available)

If you use Results.chunked or Results.feed you can continue to use the existing methods. These methods have been deprecated, so you may want to change your code anyway.

Step 2: Convert Enumerator to Source with an adapter

You can convert your existing Enumerator to a Source by first converting it to a reactive streams Publisher using Streams.enumeratorToPublisher, and then you can convert the publisher to a source using Source.fromPublisher, for example:

val enumerator: Enumerator[T] = ...
val source = Source.fromPublisher(Streams.enumeratorToPublisher(enumerator))

Step 3: (Optional) Rewrite to a Source

Here’s a list of some common mappings for enumerator factory methods:

Iteratees Akka Streams Notes
Enumerator.apply(a) Source.single(a)
Enumerator.apply(a, b) Source.apply(List(a, b)))
Enumerator.enumerate(seq) Source.apply(seq) seq must be immutable
Enumerator.repeat Source.repeat The repeated element is not evaluated each time in Akka Streams
Enumerator.empty Source.empty
Enumerator.unfold Source.unfold
Enumerator.generateM Source.unfoldAsync
Enumerator.fromStream StreamConverters.fromInputStream
Enumerator.fromFile StreamConverters.fromInputStream You have to create an InputStream for the java.io.File

§Migrating Iteratees to Sinks and Accumulators

Step 1: Convert using an adapter

You can convert your existing Iteratee to a Sink by first converting it to a reactive streams Subscriber using Streams.iterateeToSubscriber, and then you can convert the subscriber to a sink using Sink.fromSubscriber, for example:

val iteratee: Iteratee[T, U] = ...
val (subscriber, resultIteratee) = Streams.iterateeToSubscriber(iteratee)
val sink = Sink.fromSubscriber(subscriber)

If you need to return an Accumulator, you can instead use the Streams.iterateeToAccumulator method.

Step 2: (Optional) Rewrite to a Sink

Here’s a list of some common mappings for iteratee factory methods:

Iteratees Akka Streams Notes
Iteratee.fold Sink.fold
Iteratee.head Sink.headOption
Iteratee.getChunks Sink.seq
Iteratee.foreach Sink.foreach
Iteratee.ignore Sink.ignore
Done Sink.cancelled The materialized value can be mapped to produce the result, or if using accumulators, Accumulator.done can be used instead.

§Migrating Enumerateess to Processors

Step 1: Convert using an adapter

You can convert your existing Enumeratee to a Flow by first converting it to a reactive streams Processor using Streams.enumerateeToProcessor, and then you can convert the processor to a flow using Flow.fromProcessor, for example:

val enumeratee: Enumeratee[A, B] = ...
val flow = Flow.fromProcessor(() => Streams.enumerateeToProcessor(enumeratee))

Step 2: (Optional) Rewrite to a Flow

Here’s a list of some common mappings for enumeratee factory methods:

Iteratees Akka Streams Notes
Enumeratee.map Flow.map
Enumeratee.mapM Flow.mapAsync You have to specify the parallelism in Akka Streams, ie how many elements will be mapped in parallel at a time.
Enumeratee.mapConcat Flow.mapConcat
Enumeratee.filter Flow.filter
Enumeratee.take Flow.take
Enumeratee.takeWhile Flow.takeWhile
Enumeratee.drop Flow.drop
Enumeratee.dropWhile Flow.dropWhile
Enumeratee.collect Flow.collect

Next: Java Migration Guide


Found an error in this documentation? The source code for this page can be found here. After reading the documentation guidelines, please feel free to contribute a pull request. Have questions or advice to share? Go to our community forums to start a conversation with the community.