[
  {
    "path": ".dockerignore",
    "content": "config\n.project\n.settings\n.classpath\n.cache\n*.iml\n*.iws\n*.ipr\n.idea/\nbuild/\n*/build/\nout/\n*/bin/\nbin\ncodegen/\n*Generated*/\ntest-output/\n*/.target\ntarget\n.target\n*/target\nlogs\n.cache\ndist\nproject/project\nproject/target\ntmp\n.history\n.idea_modules\nRUNNING_PID\n.svn\n.DS_Store\n.git\n"
  },
  {
    "path": ".gitignore",
    "content": "config\n.project\n.settings\n.classpath\n.cache\n*.iml\n*.iws\n*.ipr\n.idea/\nbuild/\n*/build/\nout/\n*/bin/\nbin\ncodegen/\n*Generated*/\ntest-output/\n*/.target\ntarget\n.target\n*/target\nlogs\n.cache\ndist\nproject/project\nproject/target\ntmp\n.history\n.idea_modules\nRUNNING_PID\n.svn\n.DS_Store\n.vagrant"
  },
  {
    "path": "Dockerfile",
    "content": "# Based off an image that has JDK8 on busybox\nFROM frolvlad/alpine-oraclejdk8:cleaned\nMAINTAINER Yevgeniy Brikman <jim@ybrikman.com>\n\nRUN apk --update add bash\n\n# Set up activator\nRUN wget http://downloads.typesafe.com/typesafe-activator/1.3.2/typesafe-activator-1.3.2-minimal.zip \\\n    && unzip typesafe-activator-1.3.2-minimal.zip \\\n    && rm -f typesafe-activator-1.3.2-minimal.zip \\\n    && chmod +x activator-1.3.2-minimal/activator\nENV PATH $PATH:/activator-1.3.2-minimal\n\n# The source code will be in the /src folder\nRUN mkdir -p /src\nVOLUME /src\nWORKDIR /src\nCOPY . /src\n\n# Use a global SBT config to setup an external target directory so that the \n# compiled code isn't blown away if the user mounts a src folder from their\n# host OS.\nRUN mkdir -p /sbt-target \\\n    && mkdir -p ~/.sbt/0.13/ \\\n    && echo 'target := file(\"/sbt-target\") / s\"${name.value}-target\"' > ~/.sbt/0.13/global.sbt\n\n# Build the entire app so that all the dependencies are downloaded and all the\n# code is compiled. This will make starting the app the first time much faster.\nRUN activator dist\n\n# Expose play port\nEXPOSE 9000\n\n# Default command is to run the app\nCMD [\"activator\", \"run\"]"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Yevgeniy Brikman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Ping-Play\n\nThe ping-play project brings [BigPipe](https://www.facebook.com/note.php?note_id=389414033919) streaming to the\n[Play Framework](http://playframework.com/). It includes tools for a) splitting your pages up into small \"pagelets\",\nwhich makes it easier to maintain large websites, and b) streaming those pagelets down to the browser as soon as they\nare ready, which can significantly reduce page load time.\n\nTo fetch the data for a page, modern apps often have to make requests to multiple remote backend services (e.g. RESTful\nHTTP calls to a profile service, a search service, an ads service, etc). You then have to wait for *all* of these\nremote calls to come back before you can send *any* data back to the browser. For example, the following screen capture\nshows a page that makes 6 remote service calls, most of which complete in few hundred milliseconds, but one takes\nover 5 seconds. As a result, the time to first byte is 5 seconds, during which the user sees a completely blank page:\n\n![Page loading without BigPipe](images/without-big-pipe.gif)\n\nWith BigPipe, you can start streaming data back to the browser without waiting for the backends at all, and fill in the\npage incrementally as each backend responds. For example, the following screen capture shows the same page making the\nsame 6 remote service calls, but this time rendered using BigPipe. The header and much of the markup is sent back\ninstantly, so time to first byte is 10 milliseconds (instead of 5 seconds), static content (i.e. CSS, JS, images) can\nstart loading right away, and then, as each backend service responds, the corresponding part of the page (i.e. the\npagelet) is sent to the browser and rendered on the screen:\n\n![Page loading with BigPipe](images/with-big-pipe.gif)\n\n# Quick start\n\nTo understand how to transform your Play app to use BigPipe, it's helpful to first see an example that does *not* use\nBigPipe (note, the example is in Scala, but ping-play supports Java too!). Here is the controller code,\n[controllers/WithoutBigPipe.scala](sample-app-scala/app/controllers/WithoutBigPipe.scala), for the example mentioned\nearlier:\n\n```scala\nclass WithoutBigPipe(serviceClient: FakeServiceClient) extends Controller {\n  def index = Action.async { implicit request =>\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    val profileFuture = serviceClient.fakeRemoteCallMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallFast(\"search\")\n\n    // Wait for all the remote calls to complete\n    for {\n      profile <- profileFuture\n      graph <- graphFuture\n      feed <- feedFuture\n      inbox <- inboxFuture\n      ads <- adsFuture\n      search <- searchFuture\n    } yield {\n      // Render the template once all the data is available\n      Ok(views.html.withoutBigPipe(profile, graph, feed, inbox, ads, search))\n    }\n  }\n}\n```\n\nThis controller makes 6 remote service calls, gets back 6 `Future` objects, and when they have all redeemed, it uses\nthem to render the following template, [views/withoutBigPipe.scala.html](sample-app-common/src/main/twirl/views/withoutBigPipe.scala.html):\n\n```html\n@(profile: data.Response, graph: data.Response, feed: data.Response, inbox: data.Response, ads: data.Response, search: data.Response)\n\n<html>\n  <head>\n    <link rel=\"stylesheet\" href=\"/assets/stylesheets/main.css\">\n  </head>\n  <body>\n    <h1>Without Big Pipe</h1>\n    <table class=\"wrapper\">\n      <tr>\n        <td><div id=\"profile\">@views.html.helpers.module(profile)</div></td>\n        <td><div id=\"ads\">@views.html.helpers.module(ads)</div></td>\n        <td><div id=\"feed\">@views.html.helpers.module(feed)</div></td>\n      </tr>\n      <tr>\n        <td><div id=\"search\">@views.html.helpers.module(search)</div></td>\n        <td><div id=\"inbox\">@views.html.helpers.module(inbox)</div></td>\n        <td><div id=\"graph\">@views.html.helpers.module(graph)</div></td>\n      </tr>\n    </table>\n  </body>\n</html>\n```\n\nWhen you load this page, nothing will show up on the screen until all of the backend calls complete, which will take\nabout 5 seconds.\n\nTo transform this page to use BigPipe, you first add the big-pipe dependency to your build (note, this project requires\nPlay 2.4, Scala 2.11.6, SBT 0.13.8, and Java 8):\n\n```scala\nlibraryDependencies += \"com.ybrikman.ping\" %% \"big-pipe\" % \"0.0.13\"\n```\n\nNext, add support for the `.scala.stream` template type and some imports for it to your build:\n\n```scala\nTwirlKeys.templateFormats ++= Map(\"stream\" -> \"com.ybrikman.ping.scalaapi.bigpipe.HtmlStreamFormat\"),\nTwirlKeys.templateImports ++= Vector(\"com.ybrikman.ping.scalaapi.bigpipe.HtmlStream\", \"com.ybrikman.ping.scalaapi.bigpipe._\")\n```\n\nNow you can create streaming templates. These templates can mix normal HTML markup, which will be streamed to the\nbrowser immediately, with the `HtmlStream` class, which is a wrapper for an `Enumerator[Html]` that will be streamed\nto the browser whenever the `Enumerator` has data. Here is [views/withBigPipe.scala.stream](sample-app-common/src/main/twirl/views/withBigPipe.scala.stream),\nwhich is the streaming version of the template above:\n\n```html\n@(bigPipe: BigPipe, profile: Pagelet, graph: Pagelet, feed: Pagelet, inbox: Pagelet, ads: Pagelet, search: Pagelet)\n\n<html>\n  <head>\n    <link rel=\"stylesheet\" href=\"/assets/stylesheets/main.css\">\n    <!-- You need to include the BigPipe JavaScript at the top of the page -->\n    <script src=\"/assets/com/ybrikman/ping/big-pipe.js\"></script>\n  </head>\n  <body>\n    <h1>With Big Pipe</h1>\n    @HtmlStream.fromHtml(views.html.helpers.timing())\n\n    <!--\n      Wrap the entire body of your page with a bigPipe.render call. The pagelets parameter contains a Map from\n      Pagelet id to the HtmlStream for that Pagelet. You should put the HtmlStream for each of your Pagelets\n      into the appropriate place in the markup.\n    -->\n    @bigPipe.render { pagelets =>\n      <table class=\"wrapper\">\n        <tr>\n          <td>@pagelets(profile.id)</td>\n          <td>@pagelets(ads.id)</td>\n          <td>@pagelets(feed.id)</td>\n        </tr>\n        <tr>\n          <td>@pagelets(search.id)</td>\n          <td>@pagelets(inbox.id)</td>\n          <td>@pagelets(graph.id)</td>\n        </tr>\n      </table>\n    }\n\n    </body>\n</html>\n```\n\nThe key changes to notice from the original template are:\n\n1. Most of the markup in the page is wrapped in a call to the `BigPipe.render` method.\n2. The `BigPipe.render` method gives you a parameter, named `pagelets` in the example above, that is a `Map`\n   from Pagelet `id` to the `HtmlStream` for that Pagelet. The idea is to place the `HtmlStream` for each of your\n   Pagelets into the proper place in the markup where that Pagelet should appear.\n3. You need to include `big-pipe.js` in the `head` of the document.\n\nNow, let's look at the controller you can use with this template, called [controllers/WithBigPipe.scala](sample-app-scala/app/controllers/WithBigPipe.scala):\n\n```scala\nclass WithBigPipe(serviceClient: FakeServiceClient) extends Controller {\n\n  def index = Action {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    val profileFuture = serviceClient.fakeRemoteCallMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallFast(\"search\")\n\n    // Convert each Future into a Pagelet which will be rendered as HTML as soon as the data is available\n    val profile = HtmlPagelet(\"profile\", profileFuture.map(views.html.helpers.module.apply))\n    val graph = HtmlPagelet(\"graph\", graphFuture.map(views.html.helpers.module.apply))\n    val feed = HtmlPagelet(\"feed\", feedFuture.map(views.html.helpers.module.apply))\n    val inbox = HtmlPagelet(\"inbox\", inboxFuture.map(views.html.helpers.module.apply))\n    val ads = HtmlPagelet(\"ads\", adsFuture.map(views.html.helpers.module.apply))\n    val search = HtmlPagelet(\"search\", searchFuture.map(views.html.helpers.module.apply))\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    val bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search)\n    Ok.chunked(views.stream.withBigPipe(bigPipe, profile, graph, feed, inbox, ads, search))\n  }\n}\n```\n\nThe key changes to notice from the original controller are:\n\n1. Instead of waiting for *all* of the service calls to redeem, you render each one individually into `Html` as soon as\n   the data is available, giving you a `Future[Html]`.\n2. Each `Future[Html]`, plus the DOM id of where in the DOM it should be inserted, is wrapped in an `HtmlPagelet`\n   object.\n3. The `HtmlPagelet` objects are composed into a `BigPipe` object, and told to use client-side rendering.\n4. This `BigPipe` instance and all the `HtmlPagelet` objects are passed to the streaming template for rendering.\n\nWhen you load this page, you will see the outline of the page almost immediately, and each piece of the page will\nfill in this outline as soon as the corresponding remote service responds.\n\n# More examples\n\nThere are several BigPipe examples, including the one described above, in [sample-app-scala](sample-app-scala) and\n[sample-app-java](sample-app-java) in this repo (yes, BigPipe streaming works with both Scala and Java). You'll also\nwant to browse [sample-app-common](sample-app-common), which has some code shared by both sample apps, including all of\ntheir templates. For example, here is how to run the Scala sample app (assuming you have\n[Typesafe Activator](https://www.typesafe.com/community/core-tools/activator-and-sbt) installed already):\n\n1. `git clone` this repo.\n2. `activator shell`\n3. `project sampleAppScala`\n4. `run`\n5. Open `http://localhost:9000/withoutBigPipe` to see how long the page takes to load without BigPipe streaming.\n6. Open `http://localhost:9000/withBigPipe` to see how much faster the page loads with BigPipe streaming.\n\nCheck out the [Documentation](#Documentation) to see what APIs are available and [FAQ](#FAQ) to learn more about\nBigPipe.\n\n# Documentation\n\n## Scala vs Java\n\nBigPipe streaming is supported for both Scala and Java developers.\n\nScala developers should primarily be using classes in the `com.ybrikman.ping.scalaapi` package. In particular, use the\n`com.ybrikman.ping.scalaapi.bigpipe.HtmlPagelet` class to wrap your `Future[Html]` objects as `Pagelet` objects, and\nuse the `com.ybrikman.ping.scalaapi.bigpipe.BigPipe` class to compose and render your `Pagelet` objects. See\n[sample-app-scala](sample-app-scala) for examples.\n\nJava developers should primarily be using classes in the `com.ybrikman.ping.javaapi` package. In particular, use the\n`com.ybrikman.ping.javaapi.bigpipe.HtmlPagelet` class to wrap your and `Promise<Html>` as `Pagelet` objects and use the\n`com.ybrikman.ping.javaapi.bigpipe.BigPipe` class to compose and render your `Pagelet` objects. See\n[sample-app-java](sample-app-java) for examples.\n\n## Client-side vs server-side rendering\n\nPing-Play supports both client-side and server-side BigPipe streaming. Client-side streaming sends down the\npagelets in whatever order they complete and uses JavaScript to insert each pagelet into the correct spot in the DOM.\nThis gives you the fastest possible loading time, but it does add a dependency on JavaScript. For use cases where you\nwant to avoid JavaScript, such as slower browsers or search engine crawlers (i.e. SEO), you can use server-side\nrendering, which sends all the pagelets down already rendered as HTML and in the proper order. This will have a longer\npage-load time than client-side rendering, but still much faster than not using BigPipe at all.\n\nThe *only* part of your code that you have to change to switch between server-side and client-side rendering is the\n`PageletRenderOptions` parameter you pass into the `BigPipe` constructor. Here is an example of how you could check\nthe `User-Agent` header and select `PageletRenderOptions.ServerSide` if you detect GoogleBot and\n`PageletRenderOptions.ClientSide` otherwise:\n\n```scala\ndef index = Action { request =>\n  // ... fetch data, create pagelets ...\n\n  val bigPipe = new BigPipe(renderOptions(request), pagelet1, pagelet2, ...)\n\n  // ... render a streaming template ...\n}\n\nprivate def renderOptions(request: RequestHeader): PageletRenderOptions = {\n  request.headers.get(HeaderNames.USER_AGENT) match {\n    case Some(header) if header.contains(\"GoogleBot\") => PageletRenderOptions.ServerSide\n    case _ => PageletRenderOptions.ClientSide\n  }\n}\n\n```\n\n## HtmlStream and .scala.stream templates\n\nPlay's built-in `.scala.html` templates are compiled into functions that append together and return `Html`, which is\njust a wrapper for a `StringBuilder`, and cannot be streamed. This is why this project introduces a new `.scala.stream`\ntemplate that appends together and returns `HtmlStream` objects, which are a wrapper for an `Enumerator[Html]`, which\ncan be streamed. Note that this new template type still uses Play's [Twirl](https://github.com/playframework/twirl)\ntemplate compiler and its syntax. The only things that are different are:\n\n1. The extension is `.scala.stream` instead of `.scala.html`.\n2. When you are using the template in a controller, the package name will be `views.stream.XXX` instead of\n   `views.html.XXX`.\n3. To include raw, unescaped HTML, instead of wrapping the content in an `Html` object (e.g.\n   `Html(someStringWithMarkup)`), wrap it in an `HtmlStream` object (e.g. `HtmlStream.fromString(someStringWithMarkup)`).\n4. You can include an `HtmlStream` object anywhere in the markup of a `.scala.stream` template and Play will stream the\n   content down from the `HtmlStream`'s `Enumerator` whenever the content is available.\n\nThe last point is how you get BigPipe style streaming. The `HtmlStream` class has many helper methods to create an\n`HtmlStream`, including `fromHtml` and `fromHtmlFuture`, and to compose several streams into one, such as `interleave`.\n\n## Pagelet and BigPipe classes\n\nAlthough you can use the `HtmlStream` class directly, this project also comes with `Pagelet` and `BigPipe` classes that\noffer a higher level API for working with `HtmlStream`. The idea is to break your page down into small \"pagelets\" that\nknow how to fetch their own data independently and render themselves. For example, you might have one pagelet that\nfetches data from a profile service and knows how to render a user's profile, another pagelet that fetches data from an\nads service and knows how to render an ad unit, and so on. For each pagelet, you make your backend calls, get back\nsome `Future` (Scala) or `Promise` (Java) objects, render them into a `Future[Html]` or `Promise<Html>`, and then use\n`new HtmlPagelet(id, future)` or `new HtmlPagelet(id, promise)` to wrap them in a `Pagelet` class. You can then compose\nmultiple `Pagelet` instances together using the `BigPipe` constructor.\n\nThe `BigPipe` instance you get back has a `render` method that you use to actually render your pagelets. The `render`\nmethod processes your `Pagelets` as necessary for server-side or client-side rendering and gives you a `Map` from\n`Pagelet` id to the `HtmlStream` for that `Pagelet`. In your template, you should extract the `HtmlStream` for each of\nyour `Pagelets` from this map and put it into the proper place in the markup:\n\n```html\n@bigPipe.render { pagelets =>\n  <h2>The foo pagelet should go here</h2>\n  <div>@pagelets(fooPagelet.id)</div>\n\n  <h2>The bar pagelet should go here</h2>\n  <div>@pagelets(barPagelet.id)</div>\n}\n```\n\nWhen doing server-side rendering, the `HtmlStream` you get back from the `pagelets` `Map` will contain the fully\nrendered HTML. When doing client-side rendering, the `HtmlStream` will instead contain an empty placeholder that looks\nsomething like this:\n\n```html\n<div id=\"foo-pagelet\"></div>\n```\n\nThe actual content for your `Pagelet` will be streamed down at the very end (ie, at the bottom of all the markup you\npass to the `BigPipe.render` method) and it will be wrapped in markup that makes it invisible when it first arrives in\nthe browser. It will also include some JavaScript that knows how to extract the content and inject it into the right\nplaceholder in the DOM. This is what allows the pagelets to be sent down in any order, but still render correctly on\nthe page. The markup sent back by each `Pagelet` is in\n[com.ybrikman.bigpipe.pagelet.scala.html](big-pipe/src/main/twirl/com/ybrikman/bigpipe/pageletClientSide.scala.html)\nand looks roughly like this:\n\n```html\n<code id=\"pagelet1\"><!--Your content--></code>\n<script>BigPipe.onPagelet(\"pagelet1\");</script>\n```\n\nThe `BigPipe.onPagelet` method is part of [big-pipe.js](big-pipe/src/main/resources/public/com/ybrikman/ping/big-pipe.js),\nso make sure to include that script on every page.\n\n## big-pipe.js\n\nThe `BigPipe.onPagelet` method will extract the content from the `code` tag and call `BigPipe.renderPagelet` to render\nit client-side into the DOM node with the specified id (e.g. `pagelet1` in the example above). The default\n`BigPipe.renderPagelet` just inserts the content into the DOM using the `innerHTML` method. If you wish to use a more\nsophisticated method for client-side rendering, simply override the `BigPipe.renderPagelet` with your own:\n\n```javascript\nBigPipe.renderPagelet = function(id, content) {\n  // Provide a custom way to insert the specified content into the DOM node with the given id\n}\n```\n\nThe `id` parameter will be the id of the DOM node and `content` will be your content. Note that if your content was\nJSON instead of HTML, `big-pipe.js` will automatically call `JSON.parse` on it before passing it to you. This can be\nconvenient if you use client-side templating.\n\n## Client-side templating\n\nYou can use a client-side templating technology, such as mustache.js or handlebars.js to render most of your page\nin the browser. To do that, all you need to do is create a `Pagelet` that contains JSON (a `JsValue` for Scala\ndevelopers or `JsonNode` for Java developers) instead of HTML:\n\n```scala\nclass MoreBigPipeExamples(serviceClient: FakeServiceClient) extends Controller {\n\n  /**\n   * Instead of rendering each pagelet server-side with Play's templating, you can send back JSON and render each\n   * pagelet with a client-side templating library such as mustache.js\n   *\n   * @return\n   */\n  def clientSideTemplating = Action {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    val profileFuture = serviceClient.fakeRemoteCallJsonMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallJsonMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallJsonSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallJsonSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallJsonFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallJsonFast(\"search\")\n\n    // Convert each Future into a Pagelet which will send the JSON to the browser as soon as it's available\n    val profile = JsonPagelet(\"profile\", profileFuture)\n    val graph = JsonPagelet(\"graph\", graphFuture)\n    val feed = JsonPagelet(\"feed\", feedFuture)\n    val inbox = JsonPagelet(\"inbox\", inboxFuture)\n    val ads = JsonPagelet(\"ads\", adsFuture)\n    val search = JsonPagelet(\"search\", searchFuture)\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    val bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search)\n    Ok.chunked(views.stream.clientSideTemplating(bigPipe, profile, graph, feed, inbox, ads, search))\n  }\n}\n```\n\nNext, create your custom `BigPipe.renderPagelet` method:\n\n```javascript\n// Override the original BigPipe.renderPagelet method with one that uses mustache.js for client-side rendering\nBigPipe.renderPagelet = function(id, json) {\n  var domElement = document.getElementById(id);\n  if (domElement) {\n    domElement.innerHTML = Mustache.render(template, json);\n  } else {\n    console.log(\"ERROR: cannot render pagelet because DOM node with id \" + id + \" does not exist\");\n  }\n};\n```\n\nSee the `clientSideTemplating` method in\n[controllers/MoreBigPipeExamples.scala](sample-app-scala/app/controllers/MoreBigPipeExamples.scala) (Scala developers) or\n[controllers/MoreBigPipeExamples.java](sample-app-java/app/controllers/MoreBigPipeExamples.java) (Java developers) and\n[big-pipe-with-mustache.js](sample-app-common/src/main/resources/public/javascripts/big-pipe-with-mustache.js) for working examples.\n\n## Composing independent pagelets\n\nTODO: write documentation\n\n## De-duping remote service calls\n\nIf your page is built out of composable, independent pagelets, then each pagelet will know how to fetch all the data it\nneeds from backend services. If each pagelet is truly independent, that means you may have duplicated service calls.\nFor example, several pagelets may make the exact same backend call to fetch the current user's profile. This is\ninefficient and increases the load on downstream services.\n\nThis project comes with a `DedupingCache` library that makes it easy to *de-dupe* service calls. You can use it to\nensure that if several pagelets request the exact same data, you only make one call to a backend service, and all the\nother calls get the same cached response. This class has a single method called `get` that takes a key and a way to\ngenerate the value for that key if it isn't already in the cache.\n\nFor example, if you are using Play's `WSClient` to make remote calls, you could wrap any calls to it with this `get`\nmethod to ensure that any duplicate calls for a given URL get back a cached value:\n\n```scala\nclass ServiceClient {\n  val cache = new DedupingCache[String, Future[WSResponse]]\n\n  def makeRequest(url: String): Future[WSResponse] = {\n    cache.get(url, wsClient.url(url).get())\n  }\n}\n```\n\nSee [controllers/Deduping.scala](sample-app-scala/app/controllers/Deduping.scala) (Scala developers) or\n[controllers/Deduping.java](sample-app-java/app/controllers/Deduping.java) (Java developers) for a complete example of\nhow to setup and use the `DedupingCache`. You will also have to add the `CacheFilter` to your filter chain, as shown in\n[loader/PingApplicationLoader.scala](sample-app-scala/app/loader/PingApplicationLoader.scala) (Scala developers) or\n[loader/Filters.java](sample-app-java/app/loader/Filters.java) (Java developers).\n\n# FAQ\n\n## What are the caveats and drawbacks to BigPipe?\n\nBigPipe is not for everyone. There are some serious drawbacks and caveats you should be aware of before using it:\n\n### HTTP headers and error handling\n\nWith BigPipe streaming, you typically start sending the response back to the browser before your backend calls are\nfinished. The first part of that response is the HTTP headers and once you've sent them back to the browser, it's too\nlate to change your mind. If one of those backend calls fails, you've already sent your 200 OK, so you can no longer\njust send the browser a 500 error or a redirect!\n\nInstead, you must handle errors by injecting JavaScript code into your stream that displays the message when it arrives\nin the browser or redirects the user as necessary. See the `errorHandling` method in\n[controllers/MoreBigPipeExamples.scala](sample-app-scala/app/controllers/MoreBigPipeExamples.scala) (Scala developers) or\n[controllers/MoreBigPipeExamples.java](sample-app-java/app/controllers/MoreBigPipeExamples.java) (Java developers) for\na working example.\n\n### Caching\n\nBecause of the the way headers and error handling work, be extra careful using BigPipe if you cache entire\npages, especially at the CDN level. Otherwise, you may stream out a 200 OK to the CDN, hit an error with a backend call,\nand accidentally end up caching a page with an error on it.\n\nIf your pages are mostly static and can be cached for a long time (e.g. blogs), BigPipe is probably not for you. If\nyour pages are mostly dynamic and cannot be cached (e.g. the news feeds at Facebook, LinkedIn, Twitter), then BigPipe\ncan help.\n\n### Pop-in\n\nPagelets can be sent down to the browser and rendered client-side in any order. Therefore, you have to be careful to\navoid too much \"pop-in\", where rendering each pagelet causes random parts of the page to pop in and move around, which\nmakes the page hard to use.\n\nTo avoid annoying your users, use CSS to size the placeholder elements appropriately so they don't resize or move much\nas the actual content pops in. Alternatively, use JavaScript to ensure that the elements on a page render from top to\nbottom, even if they show up in a different order (e.g. set `display: none` until all the pagelets above the current\none have been filled in).\n\n## Why not AJAX?\n\nYou could try to accomplish something similar to BigPipe by sending back a page that's empty and makes lots of AJAX\ncalls to fill in each pagelet. This approach is much slower than BigPipe for a number of reasons:\n\n1. Each AJAX call requires an extra roundtrip to your server, which adds a lot of latency. This latency is especially\n   bad on mobile or slower connections.\n2. Each extra roundtrip also increases the load on your server. Instead of 1 QPS to load a page, you now have 6 QPS to\n   load a page with 6 pagelets.\n3. Older browsers severly limit how many AJAX calls you can do and most browsers give AJAX calls a low priority during\n   the initial page load.\n4. You have to download, parse, and execute a bunch of JavaScript code before you can even make the AJAX calls.\n5. It only works with JavaScript enabled.\n\nBigPipe gives you all the benefits of an AJAX portal, but without the downsides, by using a single connection&mdash;that\nis, the original connection used to request the page&mdash;and streaming down each pagelet using\n[HTTP Chunked Encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding), which works in almost all browsers.\n\n## Where can I find more info?\n\n1. [Composable and Streamable Play Apps](https://engineering.linkedin.com/play/composable-and-streamable-play-apps):\n   a talk that introduces how BigPipe streaming works on top of Play (see the\n   [video](https://www.youtube.com/watch?v=4b1XLka0UIw) and\n   [slides](http://www.slideshare.net/brikis98/composable-and-streamable-play-apps)).\n2. [BigPipe: Pipelining web pages for high performance](https://www.facebook.com/note.php?note_id=389414033919): the\n   original blog post by Facebook that introduces BigPipe on PHP.\n3. [New technologies for the new LinkedIn home page](http://engineering.linkedin.com/frontend/new-technologies-new-linkedin-home-page):\n   the new LinkedIn homepage is using BigPipe style streaming with Play. This ping-play project is loosely based off of\n   the work done originally at LinkedIn.\n\n# Project info\n\n## Status\n\nThis project is in alpha status. It has been used on small projects and is reasonably well coded, tested, and\ndocumented, but it needs more real world usage before it can be considered a mature library. Until the project hits\nversion 1.0.0, backwards compatibility is *not* guaranteed, so expect APIs to change.\n\n## Contributing\n\nContributions in the form of bug reports and pull requests are very welcome.\nCheck out the [help wanted label](https://github.com/brikis98/ping-play/labels/help%20wanted)\nfor ideas.\n\nAlso, if you're using this project in production, [drop me a line](mailto:jim@ybrikman.com),\nas I'd love to hear about your experiences!\n\n## Changelog\n\n### 0.13 (10/22/15)\n\n* Fix issue where the pagelet body was not being escaped correctly\n\n### 0.12 (07/06/15)\n\n* Added support for server-side rendering.\n* Refactored the `Pagelet` API into a trait and subclasses\n* Added the `BigPipe` class for composing and rendering `Pagelets`\n\n### 0.11 (06/30/15)\n\n* First public release.\n\n## Release process\n\nThis project is published to Sonatype as described in the\n[SBT Deploying to Sonatype](http://www.scala-sbt.org/release/docs/Using-Sonatype.html) documentation. To do that, this\nproject uses the [sbt-sonatype](https://github.com/xerial/sbt-sonatype), [sbt-pgp](http://www.scala-sbt.org/sbt-pgp),\nand [sbt-release](https://github.com/sbt/sbt-release) plugins.\n\nTo release a new version:\n\n1. Add an entry to the [Changelog](#Changelog) in this README.\n2. Make sure your PGP keys are setup\n([docs here](http://www.scala-sbt.org/release/docs/Using-Sonatype.html#First+-+PGP+Signatures))\n3. Run the SBT `release` command:\n\n```\nactivator shell\nset credentials += Credentials(\"Sonatype Nexus Repository Manager\", \"oss.sonatype.org\", \"<username>\", \"<password>\")\nrelease\n```\n\nCurrently, only the maintainer, [Yevgeniy Brikman](http://www.ybrikman.com) has the credentials for publishing new\nversions.\n\n## TODO\n\n1. The implementation, tests, and documentation for \"composing\" pagelets are not\n   yet finished. See [#18](https://github.com/brikis98/ping-play/issues/18). Note, you can find an implementation of composable\n   pagelets in the [splink/pagelets](https://github.com/splink/pagelets) library. Perhaps the two can be merged?\n2. There are a number of feature requests. See the [enhancement label](https://github.com/brikis98/ping-play/labels/enhancement)\n   in issues.\n\n# License\n\nThis code is available under the MIT license. See the LICENSE file for more info.\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/BigPipe.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\nimport play.libs.HttpExecution;\nimport scala.collection.JavaConverters;\nimport scala.concurrent.ExecutionContext;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * This class composes the given Pagelets together and prepares them for either out-of-order client-side rendering (if\n * renderOptions is set to ClientSide) or in-order server-side rendering (if renderOptions is set to ServerSide). Use\n * the render method in this class in your templates to actually render the Pagelets. It provides you a Map from\n * Pagelet id to the HtmlStream for that Pagelet. Insert the HtmlStream in this Map for each Pagelet into the\n * appropriate part of your markup.\n */\npublic class BigPipe extends com.ybrikman.ping.scalaapi.bigpipe.BigPipe {\n\n  public BigPipe(PageletRenderOptions renderOptions, List<Pagelet> pagelets, ExecutionContext ec) {\n    super(renderOptions, toScalaPagelets(pagelets, ec), ec);\n  }\n\n  public BigPipe(PageletRenderOptions renderOptions, List<Pagelet> pagelets) {\n    this(renderOptions, pagelets, HttpExecution.defaultContext());\n  }\n\n  public BigPipe(PageletRenderOptions renderOptions, Pagelet ... pagelets) {\n    this(renderOptions, Arrays.asList(pagelets));\n  }\n\n  private static scala.collection.immutable.List<com.ybrikman.ping.scalaapi.bigpipe.Pagelet> toScalaPagelets(List<Pagelet> pagelets, ExecutionContext ec) {\n    List<com.ybrikman.ping.scalaapi.bigpipe.Pagelet> scalaPagelets =\n        pagelets\n          .stream()\n          .map(pagelet -> pagelet.wrapped(ec))\n          .collect(Collectors.toList());\n\n    return JavaConverters.asScalaBufferConverter(scalaPagelets).asScala().toList();\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/HtmlPagelet.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\nimport play.libs.F;\nimport play.twirl.api.Html;\nimport scala.concurrent.ExecutionContext;\n\n/**\n * A Pagelet that contains HTML. Both server-side and client-side rendering are supported.\n */\npublic class HtmlPagelet implements Pagelet {\n\n  private final String id;\n  private final F.Promise<Html> content;\n\n  public HtmlPagelet(String id, F.Promise<Html> content) {\n    this.id = id;\n    this.content = content;\n  }\n\n  @Override\n  public String id() {\n    return id;\n  }\n\n  @Override\n  public com.ybrikman.ping.scalaapi.bigpipe.Pagelet wrapped(ExecutionContext ec) {\n    return new com.ybrikman.ping.scalaapi.bigpipe.HtmlPagelet(id, content.wrapped());\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/HtmlStreamHelper.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\nimport com.ybrikman.ping.scalaapi.bigpipe.HtmlStream;\nimport com.ybrikman.ping.scalaapi.bigpipe.JavaAdapter;\nimport play.api.http.ContentTypeOf;\nimport play.api.http.Writeable;\nimport play.api.libs.iteratee.Enumerator;\nimport play.api.mvc.Codec;\nimport play.libs.F.Promise;\nimport play.libs.HttpExecution;\nimport play.mvc.Result;\nimport play.mvc.Results;\nimport play.twirl.api.Html;\nimport scala.collection.JavaConverters;\nimport scala.concurrent.ExecutionContext;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class HtmlStreamHelper {\n\n  public static HtmlStream empty() {\n    return HtmlStream.empty();\n  }\n\n  public static HtmlStream fromString(String str) {\n    return HtmlStream.fromString(str);\n  }\n\n  public static HtmlStream fromHtml(Html html) {\n    return HtmlStream.fromHtml(html);\n  }\n\n  public static HtmlStream fromHtmlEnumerator(Enumerator<Html> enumerator) {\n    return HtmlStream.fromHtmlEnumerator(enumerator);\n  }\n\n  public static HtmlStream fromHtmlPromise(Promise<Html> html) {\n    return fromHtmlPromise(html, HttpExecution.defaultContext());\n  }\n\n  public static HtmlStream fromHtmlPromise(Promise<Html> html, ExecutionContext ec) {\n    return HtmlStream.fromHtmlFuture(html.wrapped(), ec);\n  }\n\n  public static HtmlStream fromResult(Result result) {\n    return fromResult(result, HttpExecution.defaultContext(), Codec.utf_8());\n  }\n\n  public static HtmlStream fromResult(Result result, ExecutionContext ec, Codec codec) {\n    return HtmlStream.fromResult(result.toScala(), ec, codec);\n  }\n\n  public static HtmlStream fromResultPromise(Promise<Result> result) {\n    return fromResultPromise(result, HttpExecution.defaultContext());\n  }\n\n  public static HtmlStream fromResultPromise(Promise<Result> result, ExecutionContext ec) {\n    return HtmlStream.fromResultFuture(result.map(Result::toScala, ec).wrapped(), ec);\n  }\n\n  public static HtmlStream flatten(Promise<HtmlStream> stream) {\n    return flatten(stream, HttpExecution.defaultContext());\n  }\n\n  public static HtmlStream flatten(Promise<HtmlStream> stream, ExecutionContext ec) {\n    return HtmlStream.flatten(stream.wrapped(), ec);\n  }\n\n  public static HtmlStream interleave(HtmlStream ... streams) {\n    return interleave(Arrays.asList(streams));\n  }\n\n  public static HtmlStream interleave(List<HtmlStream> streams) {\n    return HtmlStream.interleave(JavaConverters.asScalaBufferConverter(streams).asScala());\n  }\n\n  public static Results.Chunks<Html> toChunks(HtmlStream stream) {\n    return toChunks(stream, Codec.utf_8(), HttpExecution.defaultContext());\n  }\n\n  public static Results.Chunks<Html> toChunks(HtmlStream stream, Codec codec, ExecutionContext executionContext) {\n    return new Results.Chunks<Html>(Writeable.writeableOf_Content(codec, ContentTypeOf.contentTypeOf_Html(codec))) {\n      @Override\n      public void onReady(Out<Html> out) {\n        JavaAdapter.writeEnumeratorToOut(stream.enumerator(), out, executionContext);\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/JsonPagelet.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.ybrikman.ping.scalaapi.bigpipe.JavaAdapter;\nimport play.libs.F;\nimport scala.concurrent.ExecutionContext;\n\n/**\n * A Pagelet that contains JSON. The general usage pattern is to send this JSON to the browser and render it using a\n * client-side templating language, such as Mustache.js. Therefore, this Pagelet only supports client-side rendering\n * and will throw an exception if you try to render it server-side.\n */\npublic class JsonPagelet implements Pagelet {\n  private final String id;\n  private final F.Promise<JsonNode> content;\n\n  public JsonPagelet(String id, F.Promise<JsonNode> content) {\n    this.id = id;\n    this.content = content;\n  }\n\n  @Override\n  public String id() {\n    return id;\n  }\n\n  @Override\n  public com.ybrikman.ping.scalaapi.bigpipe.Pagelet wrapped(ExecutionContext ec) {\n    return new com.ybrikman.ping.scalaapi.bigpipe.JsonPagelet(id, content.map(JavaAdapter::jsonNodeToJsValue, ec).wrapped());\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/Pagelet.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\nimport com.ybrikman.ping.scalaapi.bigpipe.HtmlStream;\nimport play.libs.HttpExecution;\nimport scala.concurrent.ExecutionContext;\n\n/**\n * The base interface for \"pagelets\", which represent small, self-contained pieces of a page that can be rendered\n * independently.\n */\npublic interface Pagelet extends com.ybrikman.ping.scalaapi.bigpipe.Pagelet {\n\n  default public HtmlStream renderPlaceholder() {\n    return renderPlaceholder(HttpExecution.defaultContext());\n  }\n\n  default public HtmlStream renderServerSide() {\n    return renderServerSide(HttpExecution.defaultContext());\n  }\n\n  default public HtmlStream renderClientSide() {\n    return renderClientSide(HttpExecution.defaultContext());\n  }\n\n  @Override\n  default public HtmlStream renderPlaceholder(ExecutionContext ec) {\n    return wrapped(ec).renderPlaceholder(ec);\n  }\n\n  @Override\n  default public HtmlStream renderServerSide(ExecutionContext ec) {\n    return wrapped(ec).renderServerSide(ec);\n  }\n\n  @Override\n  default public HtmlStream renderClientSide(ExecutionContext ec) {\n    return wrapped(ec).renderClientSide(ec);\n  }\n\n  com.ybrikman.ping.scalaapi.bigpipe.Pagelet wrapped(ExecutionContext ec);\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/PageletContentType.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\n/**\n * The supported content types that you can have in a pagelet for BigPipe style streaming.\n */\npublic enum PageletContentType {\n  json,\n  html,\n  text\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/PageletRenderOptions.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\n/**\n * Specify the type of rendering you wish to use with BigPipe: either client-side, out-of-order rendering, which\n * gives the minimal load time for your page, but requires JavaScript, or server-side, in-order rendering, which has\n * a higher load time (albeit still faster than not using BigPipe at all), but does not rely on JavaScript.\n */\npublic enum PageletRenderOptions {\n  ClientSide,\n  ServerSide\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/bigpipe/TextPagelet.java",
    "content": "package com.ybrikman.ping.javaapi.bigpipe;\n\nimport play.libs.F;\nimport scala.concurrent.ExecutionContext;\n\n/**\n * A Pagelet that contains plain text. Both server-side and client-side rendering are supported.\n */\npublic class TextPagelet implements Pagelet {\n  private final String id;\n  private final F.Promise<String> content;\n\n  public TextPagelet(String id, F.Promise<String> content) {\n    this.id = id;\n    this.content = content;\n  }\n\n  @Override\n  public String id() {\n    return id;\n  }\n\n  @Override\n  public com.ybrikman.ping.scalaapi.bigpipe.Pagelet wrapped(ExecutionContext ec) {\n    return new com.ybrikman.ping.scalaapi.bigpipe.TextPagelet(id, content.wrapped());\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/dedupe/CacheFilter.java",
    "content": "package com.ybrikman.ping.javaapi.dedupe;\n\nimport com.ybrikman.ping.scalaapi.bigpipe.JavaAdapter;\nimport com.ybrikman.ping.scalaapi.dedupe.BeforeAndAfterFilter;\nimport play.libs.HttpExecution;\nimport play.mvc.Http;\nimport scala.concurrent.ExecutionContext;\n\n/**\n * Any time you use the DedupingCache, you must add this CacheFilter to your filter chain. This filter will initialize\n * the cache for each incoming request and cleanup the cache after you're done processing the request. To avoid memory\n * leaks, you want to be sure this filter runs on every single request, so it's a good idea to make it the very first\n * one in the filter chain, so no other filter can bypass it.\n *\n * @param <K>\n * @param <V>\n */\npublic class CacheFilter<K, V> extends BeforeAndAfterFilter {\n  public CacheFilter(DedupingCache<K, V> cache) {\n    this(cache, HttpExecution.defaultContext());\n  }\n\n  public CacheFilter(DedupingCache<K, V> cache, ExecutionContext executionContext) {\n    super(\n        JavaAdapter.javaConsumerToScalaFunction(rh -> cache.initCacheForRequest(contextFromRequestHeader(rh))),\n        JavaAdapter.javaConsumerToScalaFunction(rh -> cache.cleanupCacheForRequest(contextFromRequestHeader(rh))),\n        executionContext);\n  }\n\n  private static Http.Context contextFromRequestHeader(play.api.mvc.RequestHeader rh) {\n    return new Http.Context(new Http.RequestImpl(rh));\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/dedupe/DedupingCache.java",
    "content": "package com.ybrikman.ping.javaapi.dedupe;\n\nimport com.ybrikman.ping.scalaapi.bigpipe.JavaAdapter;\nimport com.ybrikman.ping.scalaapi.dedupe.Cache;\nimport com.ybrikman.ping.scalaapi.dedupe.CacheNotInitializedException;\nimport play.mvc.Http;\n\nimport javax.inject.Singleton;\nimport java.util.function.Supplier;\n\n/**\n * A cache you can use to de-dupe expensive calculations to ensure that the same calculation happens at most once while\n * processing any incoming request. For example, imagine you get an incoming request to /foo, and to render this page,\n * you have to make a dozen calls to remote services (e.g. a profile service, a search service, etc) using REST over\n * HTTP. You can use the DedupingCache to ensure you don't make the same exact call more than once (e.g. fetch the\n * exact same profile multiple times) by running all the calls through this client. You would use the URL of the REST\n * call as the key and the Future returned from the call as the value:\n *\n * String remoteUrl = \"http://example.com/some/remote/service\";\n * Promise<Response> promise = dedupingCache.get(remoteUrl, WS.url(remoteUrl).get());\n *\n * While processing any incoming request, using the code above ensures that you will not make multiple calls to the\n * exact same remoteUrl; any duplicates will just return a Promise object that is already in the cache.\n *\n * You should only use the DedupingCache for data that is safe to cache. For example, HTTP GET calls are usually safe\n * to cache since they should be idempotent, but HTTP POST calls are not safe to cache. Also, you need to add the\n * CacheFilter to your filter chain so that it can clean up the cache after you're done processing an incoming request.\n * Otherwise, you'll have a memory leak.\n *\n * @param <K> The type to use for keys. This type must define an equals and hashCode method. For example, if you're\n *            making REST over HTTP calls, the HTTP URL is a good key.\n * @param <V> The type of value that will be returned. For example, if you're making REST over HTTP calls using Play's\n *            WS library, a Promise<Response> might be a good type for the value.\n */\n@Singleton\npublic class DedupingCache<K, V> {\n  private final Cache<Long, Cache<K, V>> cache;\n\n  public DedupingCache() {\n    cache = new Cache<>();\n  }\n\n  /**\n   * Get the value for key K from the cache. If the value is not already in teh cache, use the valueIfMissing function\n   * to calculate a value, store it in the cache, and return that value.\n   *\n   * @param key\n   * @param valueIfMissing\n   * @return\n   */\n  public V get(K key, Supplier<V> valueIfMissing) {\n    return get(key, valueIfMissing, Http.Context.current());\n  }\n\n  /**\n   * Get the value for key K from the cache. If the value is not already in teh cache, use the valueIfMissing function\n   * to calculate a value, store it in the cache, and return that value. This version of the method allows you to\n   * explicitly specify the HTTP context.\n   *\n   * @param key\n   * @param valueIfMissing\n   * @param context\n   * @return\n   */\n  public V get(K key, Supplier<V> valueIfMissing, Http.Context context) {\n    return getCacheForPlayRequest(context).getOrElseUpdate(key, JavaAdapter.javaSupplierToScalaFunction(valueIfMissing));\n  }\n\n  /**\n   * Initialize the cache for the given incoming request. Should only be used by the CacheFilter.\n   *\n   * @param context\n   */\n  public void initCacheForRequest(Http.Context context) {\n    cache.put(context.id(), new Cache<>());\n  }\n\n  /**\n   * Cleanup the cache after you're completely done processing an incoming request. This is necessary to prevent memory\n   * leaks. Should only be used by the CacheFilter.\n   *\n   * @param context\n   */\n  public void cleanupCacheForRequest(Http.Context context) {\n    cache.remove(context.id());\n  }\n\n  private Cache<K, V> getCacheForPlayRequest(Http.Context context) {\n    return cache.get(context.id()).getOrElse(JavaAdapter.javaSupplierToScalaFunction(() -> {\n      throw new CacheNotInitializedException(\n          \"No cache found for request with id \" + context.id() + \" Did you add \" +\n              CacheFilter.class.getName() + \" to your filter chain?\");\n    }));\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Function2.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\n@FunctionalInterface\npublic interface Function2<A, B, R> {\n  public R apply(A a, B b);\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Function3.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\n@FunctionalInterface\npublic interface Function3<A, B, C, R> {\n  public R apply(A a, B b, C c);\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Function4.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\n@FunctionalInterface\npublic interface Function4<A, B, C, D, R> {\n  public R apply(A a, B b, C c, D d);\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Function5.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\n@FunctionalInterface\npublic interface Function5<A, B, C, D, E, R> {\n  public R apply(A a, B b, C c, D d, E e);\n}"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Function6.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\n@FunctionalInterface\npublic interface Function6<A, B, C, D, E, F, R> {\n  public R apply(A a, B b, C c, D d, E e, F f);\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Promise2.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\nimport play.libs.F;\n\npublic class Promise2<A, B> {\n\n  private final F.Promise<A> a;\n  private final F.Promise<B> b;\n\n  public Promise2(F.Promise<A> a, F.Promise<B> b) {\n    this.a = a;\n    this.b = b;\n  }\n\n  public <R> F.Promise<R> map(Function2<A, B, R> function) {\n    return a.flatMap(a -> b.map(b -> function.apply(a, b)));\n  }\n\n  public <R> F.Promise<R> flatMap(Function2<A, B, F.Promise<R>> function) {\n    return a.flatMap(a -> b.flatMap(b -> function.apply(a, b)));\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Promise3.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\nimport play.libs.F;\n\npublic class Promise3<A, B, C> {\n\n  private final F.Promise<A> a;\n  private final F.Promise<B> b;\n  private final F.Promise<C> c;\n\n  public Promise3(F.Promise<A> a, F.Promise<B> b, F.Promise<C> c) {\n    this.a = a;\n    this.b = b;\n    this.c = c;\n  }\n\n  public <R> F.Promise<R> map(Function3<A, B, C, R> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.map(c -> function.apply(a, b, c))));\n  }\n\n  public <R> F.Promise<R> flatMap(Function3<A, B, C, F.Promise<R>> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.flatMap(c -> function.apply(a, b, c))));\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Promise4.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\nimport play.libs.F;\n\npublic class Promise4<A, B, C, D> {\n\n  private final F.Promise<A> a;\n  private final F.Promise<B> b;\n  private final F.Promise<C> c;\n  private final F.Promise<D> d;\n\n  public Promise4(F.Promise<A> a, F.Promise<B> b, F.Promise<C> c, F.Promise<D> d) {\n    this.a = a;\n    this.b = b;\n    this.c = c;\n    this.d = d;\n  }\n\n  public <R> F.Promise<R> map(Function4<A, B, C, D, R> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.flatMap(c -> d.map(d -> function.apply(a, b, c, d)))));\n  }\n\n  public <R> F.Promise<R> flatMap(Function4<A, B, C, D, F.Promise<R>> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.flatMap(c -> d.flatMap(d -> function.apply(a, b, c, d)))));\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Promise5.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\nimport play.libs.F;\n\npublic class Promise5<A, B, C, D, E> {\n\n  private final F.Promise<A> a;\n  private final F.Promise<B> b;\n  private final F.Promise<C> c;\n  private final F.Promise<D> d;\n  private final F.Promise<E> e;\n\n  public Promise5(F.Promise<A> a, F.Promise<B> b, F.Promise<C> c, F.Promise<D> d, F.Promise<E> e) {\n    this.a = a;\n    this.b = b;\n    this.c = c;\n    this.d = d;\n    this.e = e;\n  }\n\n  public <R> F.Promise<R> map(Function5<A, B, C, D, E, R> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.flatMap(c -> d.flatMap(d -> e.map(e -> function.apply(a, b, c, d, e))))));\n  }\n\n  public <R> F.Promise<R> flatMap(Function5<A, B, C, D, E, F.Promise<R>> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.flatMap(c -> d.flatMap(d -> e.flatMap(e -> function.apply(a, b, c, d, e))))));\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/Promise6.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\nimport play.libs.F;\n\npublic class Promise6<A, B, C, D, E, F> {\n\n  private final play.libs.F.Promise<A> a;\n  private final play.libs.F.Promise<B> b;\n  private final play.libs.F.Promise<C> c;\n  private final play.libs.F.Promise<D> d;\n  private final play.libs.F.Promise<E> e;\n  private final play.libs.F.Promise<F> f;\n\n  public Promise6(play.libs.F.Promise<A> a, play.libs.F.Promise<B> b, play.libs.F.Promise<C> c, play.libs.F.Promise<D> d, play.libs.F.Promise<E> e, play.libs.F.Promise<F> f) {\n    this.a = a;\n    this.b = b;\n    this.c = c;\n    this.d = d;\n    this.e = e;\n    this.f = f;\n  }\n\n  public <R> play.libs.F.Promise<R> map(Function6<A, B, C, D, E, F, R> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.flatMap(c -> d.flatMap(d -> e.flatMap(e -> f.map(f -> function.apply(a, b, c, d, e, f)))))));\n  }\n\n  public <R> play.libs.F.Promise<R> flatMap(Function6<A, B, C, D, E, F, play.libs.F.Promise<R>> function) {\n    return a.flatMap(a -> b.flatMap(b -> c.flatMap(c -> d.flatMap(d -> e.flatMap(e -> f.flatMap(f -> function.apply(a, b, c, d, e, f)))))));\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/java/com/ybrikman/ping/javaapi/promise/PromiseHelper.java",
    "content": "package com.ybrikman.ping.javaapi.promise;\n\nimport play.libs.F.Promise;\n\npublic class PromiseHelper {\n\n  public static <A, B> Promise2<A, B> sequence(Promise<A> a, Promise<B> b) {\n    return new Promise2<>(a, b);\n  }\n\n  public static <A, B, C> Promise3<A, B, C> sequence(Promise<A> a, Promise<B> b, Promise<C> c) {\n    return new Promise3<>(a, b, c);\n  }\n\n  public static <A, B, C, D> Promise4<A, B, C, D> sequence(Promise<A> a, Promise<B> b, Promise<C> c, Promise<D> d) {\n    return new Promise4<>(a, b, c, d);\n  }\n\n  public static <A, B, C, D, E> Promise5<A, B, C, D, E> sequence(Promise<A> a, Promise<B> b, Promise<C> c, Promise<D> d, Promise<E> e) {\n    return new Promise5<>(a, b, c, d, e);\n  }\n\n  public static <A, B, C, D, E, F> Promise6<A, B, C, D, E, F> sequence(Promise<A> a, Promise<B> b, Promise<C> c, Promise<D> d, Promise<E> e, Promise<F> f) {\n    return new Promise6<>(a, b, c, d, e, f);\n  }\n}\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "big-pipe/src/main/resources/public/com/ybrikman/ping/big-pipe.js",
    "content": "/**\n * JavaScript helper functions for BigPipe-style streaming. This code has no external dependencies.\n */\n(function() {\n  \"use strict\";\n\n  var JSON_CONTENT_TYPE = \"json\";\n  var HTML_CONTENT_TYPE = \"html\";\n  var TEXT_CONTENT_TYPE = \"text\";\n\n  var root = this;\n  var BigPipe = root.BigPipe = {};\n  var document = root.document;\n  var console = root.console;\n\n  var log = function(msg) {\n    if (console && console.log) {\n      console.log(msg);\n    }\n  };\n\n  BigPipe.unescapeForEmbedding = function(str) {\n    if (str) {\n      return str.replace(new RegExp('\\\\\\\\u002d\\\\\\\\u002d', \"gi\"), '--');\n    } else {\n      return str;\n    }\n  };\n\n  BigPipe.readEmbeddedContentFromDom = function(domId) {\n    var contentElem = document.getElementById(domId);\n    if (contentElem) {\n      return BigPipe.unescapeForEmbedding(contentElem.firstChild.nodeValue);\n    } else {\n      log(\"ERROR: Unable to read content from DOM node with id \" + domId + \" so return an empty String.\");\n      return \"\";\n    }\n  };\n\n  BigPipe.parseEmbeddedJsonFromDom = function(domId) {\n    var content = BigPipe.readEmbeddedContentFromDom(domId);\n    return JSON.parse(content);\n  };\n\n  BigPipe.renderPagelet = function(id, content) {\n    var domElement = document.getElementById(id);\n    if (domElement) {\n      domElement.innerHTML = content;\n    } else {\n      log(\"ERROR: cannot insert pagelet content because DOM node with id \" + id + \" does not exist\");\n    }\n  };\n\n  BigPipe.onPagelet = function(id, contentId, contentType) {\n    if (contentType === JSON_CONTENT_TYPE) {\n      var json = BigPipe.parseEmbeddedJsonFromDom(contentId);\n      BigPipe.renderPagelet(id, json);\n    } else if (contentType === HTML_CONTENT_TYPE || contentType === TEXT_CONTENT_TYPE) {\n      var content = BigPipe.readEmbeddedContentFromDom(contentId);\n      BigPipe.renderPagelet(id, content);\n    } else {\n      log(\"ERROR: unsupported contentType \" + contentType + \" for pagelet with id \" + id);\n    }\n  };\n}.call(this));"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/bigpipe/BigPipe.scala",
    "content": "package com.ybrikman.ping.scalaapi.bigpipe\n\nimport com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions\nimport com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions._\n\nimport scala.concurrent.ExecutionContext\n\n/**\n * This class composes the given Pagelets together and prepares them for either out-of-order client-side rendering (if\n * renderOptions is set to ClientSide) or in-order server-side rendering (if renderOptions is set to ServerSide). Use\n * the render method in this class in your templates to actually render the Pagelets. It provides you a Map from\n * Pagelet id to the HtmlStream for that Pagelet. Insert the HtmlStream in this Map for each Pagelet into the\n * appropriate part of your markup.\n *\n * @param renderOptions\n * @param pagelets\n * @param ec\n */\nclass BigPipe(renderOptions: PageletRenderOptions, pagelets: Pagelet*)(implicit ec: ExecutionContext) {\n\n  /**\n   * Render the Pagelets in this BigPipe. The layoutBody function will get as an argument a Map from Pagelet id to\n   * HtmlStream for that Pagelet. Insert this HtmlStream into the appropriate place in your markup.\n   *\n   * @param layoutBody\n   * @return\n   */\n  def render(layoutBody: Map[String, HtmlStream] => HtmlStream): HtmlStream = {\n    val bodyPagelets = pagelets.map { pagelet =>\n      renderOptions match {\n        case ClientSide => pagelet.id -> pagelet.renderPlaceholder\n        case ServerSide => pagelet.id -> pagelet.renderServerSide\n      }\n    }.toMap\n\n    val footerPagelets = renderOptions match {\n      case ClientSide => HtmlStream.interleave(pagelets.map(_.renderClientSide):_*)\n      case ServerSide => HtmlStream.empty\n    }\n\n    layoutBody(bodyPagelets).andThen(footerPagelets)\n  }\n}\n\n"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/bigpipe/Embed.scala",
    "content": "package com.ybrikman.ping.scalaapi.bigpipe\n\nimport play.api.libs.json.{Json, JsValue}\nimport play.twirl.api.Html\n\nobject Embed {\n\n  /**\n   * Convert the given json into an HTML String that can be safely embedded into a webpage in a way that the browser\n   * completely ignores it.\n   *\n   * @param json\n   * @return\n   */\n  def escapeForEmbedding(json: JsValue): Html = {\n    escapeForEmbedding(Json.stringify(json))\n  }\n\n  /**\n   * Convert the given HTML into an HTML String that can be safely embedded into a webpage in a way that the browser\n   * completely ignores it.\n   *\n   * @param html\n   * @return\n   */\n  def escapeForEmbedding(html: Html): Html = {\n    escapeForEmbedding(html.body)\n  }\n\n  /**\n   * Convert the given String into an HTML String that can be safely embedded into a webpage in a way that the browser\n   * completely ignores it.\n   *\n   * @param str\n   * @return\n   */\n  def escapeForEmbedding(str: String): Html = {\n    Html(escapeDashes(str))\n  }\n\n  /**\n   * To hide content from the browser, we wrap it in an HTML comment. To make sure no content can escape from that\n   * comment, the only thing we have to do is escape double dashes.\n   *\n   * @param str\n   * @return\n   */\n  private def escapeDashes(str: String): String = {\n    str.replaceAll(\"--\", \"\\\\\\\\\\\\u002d\\\\\\\\\\\\u002d\")\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/bigpipe/HtmlStream.scala",
    "content": "package com.ybrikman.ping.scalaapi.bigpipe\n\nimport play.api.libs.iteratee.{Enumeratee, Enumerator}\nimport play.twirl.api.{Appendable, HtmlFormat, Format, Html}\nimport play.api.mvc.{Codec, Result}\nimport scala.language.implicitConversions\nimport scala.concurrent.{ExecutionContext, Future}\n\n/**\n * A custom Appendable that lets you create .scala.stream templates instead of .scala.html. These templates can mix Html\n * markup with Enumerators that contain Html markup so that as soon as the content is available, Play can stream it\n * back to the client. You need to add this class as a custom template type in build.sbt.\n *\n * @param enumerator\n */\nclass HtmlStream(val enumerator: Enumerator[Html]) extends Appendable[HtmlStream] {\n  def andThen(other: HtmlStream): HtmlStream = HtmlStream.fromHtmlEnumerator(enumerator.andThen(other.enumerator))\n}\n\n/**\n * Companion object for HtmlStream that contains convenient factory and composition methods.\n */\nobject HtmlStream {\n\n  /**\n   * Create an empty HtmlStream\n   *\n   * @return\n   */\n  def empty: HtmlStream = {\n    fromString(\"\")\n  }\n\n  /**\n   * Create an HtmlStream from a String\n   *\n   * @param text\n   * @return\n   */\n  def fromString(text: String): HtmlStream = {\n    fromHtml(Html(text))\n  }\n\n  /**\n   * Create an HtmlStream from a Future that will eventually contain a String\n   *\n   * @param eventuallyString\n   * @return\n   */\n  def fromStringFuture(eventuallyString: Future[String])(implicit ec: ExecutionContext): HtmlStream = {\n    fromHtmlFuture(eventuallyString.map(Html.apply))\n  }\n\n  /**\n   * Create an HtmlStream from Html\n   *\n   * @param html\n   * @return\n   */\n  def fromHtml(html: Html): HtmlStream = {\n    fromHtmlEnumerator(Enumerator(html))\n  }\n\n  /**\n   * Create an HtmlStream from an Enumerator of Html\n   *\n   * @param enumerator\n   * @return\n   */\n  def fromHtmlEnumerator(enumerator: Enumerator[Html]): HtmlStream = {\n    new HtmlStream(enumerator)\n  }\n\n  /**\n   * Create an HtmlStream from a Future that will eventually contain Html\n   *\n   * @param eventuallyHtml\n   * @return\n   */\n  def fromHtmlFuture(eventuallyHtml: Future[Html])(implicit ec: ExecutionContext): HtmlStream = {\n    flatten(eventuallyHtml.map(fromHtml))\n  }\n\n  /**\n   * Create an HtmlStream from the body of the Result.\n   *\n   * @param result\n   * @return\n   */\n  def fromResult(result: Result)(implicit ec: ExecutionContext, codec: Codec): HtmlStream = {\n    HtmlStream.fromHtmlEnumerator(result.body.map(bytes => Html(codec.decode(bytes))))\n  }\n\n  /**\n   * Create an HtmlStream from a the body of a Future[Result].\n   *\n   * @param result\n   * @return\n   */\n  def fromResultFuture(result: Future[Result])(implicit ec: ExecutionContext): HtmlStream = {\n    flatten(result.map(fromResult))\n  }\n\n  /**\n   * Interleave multiple HtmlStreams together. Interleaving is done based on whichever HtmlStream next has input ready,\n   * if multiple have input ready, the order is undefined.\n   *\n   * @param streams\n   * @return\n   */\n  def interleave(streams: HtmlStream*): HtmlStream = {\n    fromHtmlEnumerator(Enumerator.interleave(streams.map(_.enumerator)))\n  }\n\n  /**\n   * Create an HtmlStream from a Future that will eventually contain an HtmlStream.\n   *\n   * @param eventuallyStream\n   * @return\n   */\n  def flatten(eventuallyStream: Future[HtmlStream])(implicit ec: ExecutionContext): HtmlStream = {\n    fromHtmlEnumerator(Enumerator.flatten(eventuallyStream.map(_.enumerator)))\n  }\n}\n\n/**\n * A custom Appendable that lets you create .scala.stream templates instead of .scala.html. These templates can mix Html\n * markup with Enumerators that contain Html markup so that as soon as the content is available, Play can stream it\n * back to the client.\n */\nobject HtmlStreamFormat extends Format[HtmlStream] {\n\n  def raw(text: String): HtmlStream = {\n    HtmlStream.fromString(text)\n  }\n\n  def escape(text: String): HtmlStream = {\n    raw(HtmlFormat.escape(text).body)\n  }\n\n  def empty: HtmlStream = {\n    raw(\"\")\n  }\n\n  def fill(elements: scala.collection.immutable.Seq[HtmlStream]): HtmlStream = {\n    elements.reduce((agg, curr) => agg.andThen(curr))\n  }\n}\n\n/**\n * Useful implicits when working with HtmlStreams\n */\nobject HtmlStreamImplicits {\n\n  /**\n   * Implicit conversion so HtmlStream can be passed directly to Ok.feed and Ok.chunked\n   *\n   * @param stream\n   * @param ec\n   * @return\n   */\n  implicit def toEnumerator(stream: HtmlStream)(implicit ec: ExecutionContext): Enumerator[Html] = {\n    // Skip empty chunks, as these mean EOF in chunked encoding\n    stream.enumerator.through(Enumeratee.filter(!_.body.isEmpty))\n  }\n}\n\n"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/bigpipe/JavaAdapter.scala",
    "content": "package com.ybrikman.ping.scalaapi.bigpipe\n\nimport java.util.function.{Supplier => JavaSupplier, Consumer => JavaConsumer, Function => JavaFunction}\n\nimport com.fasterxml.jackson.databind.JsonNode\nimport play.api.libs.iteratee.{Enumerator, Iteratee}\nimport play.api.libs.json.{Writes, JsValue}\nimport play.mvc.Results.Chunks.Out\n\nimport scala.concurrent.ExecutionContext\n\n/**\n * A helper class for going between Scala and Java code\n */\nobject JavaAdapter {\n\n  def writeEnumeratorToOut[A](enumerator: Enumerator[A], out: Out[A], executionContext: ExecutionContext): Unit = {\n    implicit val ec = executionContext\n    enumerator.run(Iteratee.foreach { chunk =>\n      if (!chunk.toString.isEmpty) {\n        out.write(chunk)\n      }\n    }).onComplete(_ => out.close())\n  }\n  \n  def jsonNodeToJsValue(jsonNode: JsonNode): JsValue = {\n    Writes.JsonNodeWrites.writes(jsonNode)\n  }\n\n  def javaConsumerToScalaFunction[A](consumer: JavaConsumer[A]): A => Unit = {\n    (a) => consumer.accept(a)\n  }\n\n  def javaSupplierToScalaFunction[A](supplier: JavaSupplier[A]): () => A = {\n    () => supplier.get()\n  }\n\n  def javaFunctionToScalaFunction[A, B](function: JavaFunction[A, B]): A => B = {\n    (a) => function.apply(a)\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/bigpipe/Pagelet.scala",
    "content": "package com.ybrikman.ping.scalaapi.bigpipe\n\nimport com.ybrikman.ping.javaapi.bigpipe.PageletContentType\nimport play.api.libs.json.{JsValue, Json}\nimport play.twirl.api.Html\n\nimport scala.concurrent.{ExecutionContext, Future}\nimport PageletConstants._\n\n/**\n * The base trait for \"pagelets\", which represent small, self-contained pieces of a page that can be rendered\n * independently. \n */\ntrait Pagelet {\n  /**\n   * A unique id for this Pagelet. Usually corresponds to the id in the DOM where this Pagelet should be inserted.\n   */\n  val id: String\n\n  /**\n   * Render an HTML placeholder for this Pagelet. This will be filled in later using JavaScript code when the Pagelet\n   * data is available and shows up in the browser.\n   * \n   * @param ec\n   * @return\n   */\n  def renderPlaceholder(implicit ec: ExecutionContext): HtmlStream = {\n    HtmlStream.fromHtml(com.ybrikman.bigpipe.html.pageletServerSide(id, EmptyContent))\n  }\n\n  /**\n   * Render all the HTML for this Pagelet server-side. This is typically used when the Pagelets are being streamed \n   * in-order, which is useful for clients that do not support JavaScript and search engine crawlers (i.e. SEO).\n   * \n   * @param ec\n   * @return\n   */\n  def renderServerSide(implicit ec: ExecutionContext): HtmlStream\n\n  /**\n   * Render the HTML for this Pagelet so that it's initially invisible and can be inserted into the proper place in the\n   * DOM client-side, using JavaScript. This is typically used when the Pagelets are being streamed out-of-order to \n   * minimize the load-time for a page.\n   * \n   * @param ec\n   * @return\n   */\n  def renderClientSide(implicit ec: ExecutionContext): HtmlStream\n}\n\n/**\n * A Pagelet that contains HTML. Both server-side and client-side rendering are supported.\n * \n * @param id\n * @param content\n */\ncase class HtmlPagelet(id: String, content: Future[Html]) extends Pagelet {\n  override def renderServerSide(implicit ec: ExecutionContext): HtmlStream = {\n    HtmlStream.fromHtmlFuture(content.map(str => com.ybrikman.bigpipe.html.pageletServerSide(id, str.body)))\n  }\n\n  override def renderClientSide(implicit ec: ExecutionContext): HtmlStream = {\n    HtmlStream.fromHtmlFuture(content.map(str =>\n      com.ybrikman.bigpipe.html.pageletClientSide(str.body, id, PageletContentType.html)))\n  }\n}\n\n/**\n * A Pagelet that contains JSON. The general usage pattern is to send this JSON to the browser and render it using a\n * client-side templating language, such as Mustache.js. Therefore, this Pagelet only supports client-side rendering\n * and will throw an exception if you try to render it server-side.\n * \n * @param id\n * @param content\n */\ncase class JsonPagelet(id: String, content: Future[JsValue]) extends Pagelet {\n  override def renderServerSide(implicit ec: ExecutionContext): HtmlStream = {\n    throw new UnsupportedOperationException(s\"Server-side rendering is not supported for ${getClass.getName}\")\n  }\n\n  override def renderClientSide(implicit ec: ExecutionContext): HtmlStream = {\n    HtmlStream.fromHtmlFuture(content.map(json =>\n      com.ybrikman.bigpipe.html.pageletClientSide(Json.stringify(json), id, PageletContentType.json)))\n  }\n}\n\n/**\n * A Pagelet that contains plain text. Both server-side and client-side rendering are supported.\n * \n * @param id\n * @param content\n */\ncase class TextPagelet(id: String, content: Future[String]) extends Pagelet {\n  override def renderServerSide(implicit ec: ExecutionContext): HtmlStream = {\n    HtmlStream.fromHtmlFuture(content.map(str => com.ybrikman.bigpipe.html.pageletServerSide(id, str)))\n  }\n\n  override def renderClientSide(implicit ec: ExecutionContext): HtmlStream = {\n    HtmlStream.fromHtmlFuture(content.map(str =>\n      com.ybrikman.bigpipe.html.pageletClientSide(str, id, PageletContentType.text)))\n  }\n}\n\nobject PageletConstants {\n  val EmptyContent = \"\"\n}"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/compose/Compose.scala",
    "content": "package com.ybrikman.ping.scalaapi.compose\n\nimport com.ybrikman.ping.scalaapi.bigpipe.HtmlStream\nimport play.api.http.HeaderNames\nimport play.api.libs.iteratee.Iteratee\nimport play.api.mvc.{Cookies, Cookie, Codec, Result}\nimport play.twirl.api.Html\n\nimport scala.concurrent.{Future, ExecutionContext}\n\n/**\n * Helpers for building Play apps out of standalone, composable pagelets.\n * Note: these are not yet tested or documented, so use at your own risk.\n */\nobject Compose {\n\n  val cssHeaderName = \"X-CSS\"\n  val jsHeaderName = \"X-JS\"\n\n  /**\n   * Read the body of a Result as Html. Since the body is an Enumerator and may not be available yet, this method\n   * returns a Future.\n   *\n   * @param result\n   * @param codec\n   * @return\n   */\n  def readBody(result: Result)(implicit codec: Codec, ec: ExecutionContext): Future[Html] = {\n    result.body.run(Iteratee.consume()).map(bytes => Html(new String(bytes, codec.charset)))\n  }\n\n  /**\n   * Merge all the cookies set in the given results into a single sequence.\n   *\n   * @param results\n   * @return\n   */\n  def mergeCookies(results: Result*): Seq[Cookie] = {\n    results\n      .flatMap(result => result.header.headers.get(HeaderNames.SET_COOKIE)\n      .map(Cookies.decodeSetCookieHeader)\n      .getOrElse(Seq.empty))\n  }\n\n  /**\n   * Convert the given sequences of CSS and JS into HTTP headers that can be added to the Result\n   *\n   * @param css\n   * @param js\n   * @return\n   */\n  def asHeaders(css: Seq[String], js: Seq[String]): Seq[(String, String)] = {\n    Seq(cssHeaderName -> css.mkString(\",\"), jsHeaderName -> js.mkString(\",\"))\n  }\n\n  /**\n   * Read the CSS header from each result and merge and de-dupe them into a single sequence\n   *\n   * @param results\n   * @return\n   */\n  def mergeCssHeaders(results: Result*): Seq[String] = {\n    mergeHeaderValues(cssHeaderName, parseCssHeader, results:_*)\n  }\n\n  /**\n   * Read the JS header from each the result and merge and de-dupe them into a single sequence\n   *\n   * @param results\n   * @return\n   */\n  def mergeJsHeaders(results: Result*): Seq[String] = {\n    mergeHeaderValues(jsHeaderName, parseJsHeader, results:_*)\n  }\n\n  private def mergeHeaderValues(headerName: String, parseHeader: Result => Seq[String], results: Result*): Seq[String] = {\n    results.flatMap(parseHeader).distinct\n  }\n\n  /**\n   * Read the CSS header from the given Result, which should define the CSS dependencies for the Result\n   *\n   * @param result\n   * @return\n   */\n  def parseCssHeader(result: Result): Seq[String] = parseHeader(cssHeaderName, result)\n\n  /**\n   * Read the JS header from the given Result, which should define the CSS dependencies for the Result\n   *\n   * @param result\n   * @return\n   */\n  def parseJsHeader(result: Result): Seq[String] = parseHeader(jsHeaderName, result)\n\n  /**\n   * Render the given sequence of CSS URLs as link tags\n   *\n   * @param css\n   * @return\n   */\n  def renderCssDependencies(css: Seq[String]): Html = {\n    com.ybrikman.bigpipe.html.css(css)\n  }\n\n  /**\n   * Render the given sequence of JS URLs as script tags\n   *\n   * @param js\n   * @return\n   */\n  def renderJsDependencies(js: Seq[String]): Html = {\n    com.ybrikman.bigpipe.html.js(js)\n  }\n\n  /**\n   * Merge all the JavaScript dependencies from the results into a list of script tags\n   *\n   * @param results\n   * @return\n   */\n  def mergeJsFromResults(results: Future[Result]*)(implicit ec: ExecutionContext): HtmlStream = {\n    mergeDependenciesFromResults(parseJsHeader, renderJsDependencies, results)\n  }\n\n  /**\n   * Merge all the CSS dependencies from the results into a list of link tags\n   *\n   * @param results\n   * @return\n   */\n  def mergeCssFromResults(results: Future[Result]*)(implicit ec: ExecutionContext): HtmlStream = {\n    mergeDependenciesFromResults(parseCssHeader, renderCssDependencies, results)\n  }\n\n  private def parseHeader(headerName: String, result: Result): Seq[String] = {\n    result.header.headers.get(headerName).map(_.split(\",\").toVector).getOrElse(Vector.empty)\n  }\n\n  private def mergeDependenciesFromResults(parseHeader: Result => Seq[String], render: Seq[String] => Html, resultFutures: Seq[Future[Result]])(implicit ec: ExecutionContext): HtmlStream = {\n    val allResultsFuture = Future.sequence(resultFutures)\n\n    val htmlFuture = allResultsFuture.map { results =>\n      val values = results.map(parseHeader).flatten.distinct\n      render(values)\n    }\n\n    HtmlStream.fromHtmlFuture(htmlFuture)\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/dedupe/BeforeAndAfterFilter.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport play.api.mvc.{Result, RequestHeader, Filter}\n\nimport scala.concurrent.{ExecutionContext, Future}\n\n/**\n * A filter that takes two parameters--a before function and an after function--and guarantees the before function is\n * executed before the rest of the filter chain executes and the after function is executed after the rest of the\n * filter chain (no matter what error may have been thrown).\n *\n * @param before\n * @param after\n * @param ec\n */\nclass BeforeAndAfterFilter(before: RequestHeader => Unit, after: RequestHeader => Unit)(implicit ec: ExecutionContext) extends Filter {\n  override def apply(next: (RequestHeader) => Future[Result])(playRequest: RequestHeader): Future[Result] = {\n    // Be very careful with error handling to guarantee the after code executes no matter what kind of error happened.\n    try {\n      before(playRequest)\n      next(playRequest).map { result =>\n        result.copy(body = result.body.onDoneEnumerating(after(playRequest)))\n      }.recover { case t: Throwable =>\n        after(playRequest)\n        throw t\n      }\n    } catch {\n      case t: Throwable =>\n        after(playRequest)\n        throw t\n    }\n  }\n}"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/dedupe/Cache.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport Cache._\nimport play.api.Configuration\nimport java.util.concurrent.ConcurrentHashMap\nimport collection.JavaConverters._\n\n/**\n * A Scala wrapper for a Java's ConcurrentHashMap (CHM). Exposes the basic underlying methods of CHM and adds a\n * getOrElseUpdate(key, value) method that lazily evaluates the value parameter only if the key is not already present\n * in the cache.\n *\n * You may be asking, why not just use Scala's ConcurrentMap interface, which already has a getOrElseUpdate method?\n *\n * val cache = new ConcurrentHashMap().asScala\n * cache.getOrElseUpdate(\"foo\", \"bar\") // BAD idea\n *\n * The answer is because this method is inherited from the MapLike trait, and is NOT a thread safe (atomic) operation!\n *\n * The strategy used in the class below is to wrap all values with a LazyWrapper class that only evaluates the value\n * when explicitly accessed. In the getOrElseUpdate method, we avoid accessing the passed in value unless we know it\n * was the one actually inserted into the cache.\n *\n * For more info, see: http://boundary.com/blog/2011/05/\n *\n * TODO: investigate if boundary's NonBlockingHashMap is as good as they say it is (and what tests they have to prove\n * it).\n *\n * TODO: Java-friendly API\n *\n * @param initialCapacity\n * @param concurrencyLevel\n * @param loadFactor\n * @tparam K\n * @tparam V\n */\nclass Cache[K, V](initialCapacity: Int, loadFactor: Float, concurrencyLevel: Int) {\n\n  /**\n   * Overloaded constructor that creates the cache with initial capacity, concurrency level, and load factor read from\n   * config\n   *\n   * @param config\n   * @return\n   */\n  def this(config: Configuration) = this(\n    config.getInt(CONFIG_KEY_INITIAL_CAPACITY).getOrElse(DEFAULT_INITIAL_CAPACITY),\n    config.getDouble(CONFIG_KEY_LOAD_FACTOR).map(_.toFloat).getOrElse(DEFAULT_LOAD_FACTOR),\n    config.getInt(CONFIG_KEY_CONCURRENCY_LEVEL).getOrElse(DEFAULT_CONCURRENCY_LEVEL)\n  )\n\n  /**\n   * Empty constructor that uses default values for initial capacity, concurrency level, and load factor\n   * @return\n   */\n  def this() = this(\n    DEFAULT_INITIAL_CAPACITY,\n    DEFAULT_LOAD_FACTOR,\n    DEFAULT_CONCURRENCY_LEVEL\n  )\n\n  private val cache = new ConcurrentHashMap[K, LazyWrapper[V]](initialCapacity, loadFactor, concurrencyLevel).asScala\n\n  /**\n   * Returns all elements of the cache. Use this method only if you really need all of the elements. Calling it will cause\n   * all lazy values to be calculated.\n   */\n  def getAll: Map[K, V] = {\n    val mutable = cache.map { case(key, wrapper) => key -> unwrap(wrapper) }\n    mutable.toMap\n  }\n\n  /**\n   * Returns true if this key is associated with a value in the cache and false otherwise.\n   *\n   * @param key\n   * @return\n   */\n  def contains(key: K): Boolean = {\n    cache.contains(key)\n  }\n\n  /**\n   * Optionally return the value associated with the given key\n   *\n   * @param key\n   * @return\n   */\n  def get(key: K): Option[V] = {\n    cache.get(key).map(unwrap)\n  }\n\n  /**\n   * Associate the given key with the given value. Optionally return any value previously associated with the key.\n   *\n   * @param key\n   * @param value\n   * @return\n   */\n  def put(key: K, value: V): Option[V] = {\n    cache.put(key, wrap(value)).map(unwrap)\n  }\n\n  /**\n   * If the given key is already associated with a value, return that value. Otherwise, associate the key with the\n   * given value and return None.\n   *\n   * @param key\n   * @param value\n   * @return\n   */\n  def putIfAbsent(key: K, value: V): Option[V] = {\n    cache.putIfAbsent(key, wrap(value)).map(unwrap)\n  }\n\n  /**\n   * Get the value associated with the given key. If no value is already associated, then associate the given value\n   * with the key and use it as the return value.\n   *\n   * Like Scala's ConcurrentMap, the value parameter will be lazily evaluated: that is, it'll only be evaluated if\n   * there wasn't already a value associated with the given key. However, unlike Scala's ConcurrentMap, this method is\n   * a thread safe (atomic) operation.\n   *\n   * @param key\n   * @param value\n   * @return\n   */\n  def getOrElseUpdate(key: K, value: => V): V = {\n    val newWrapper = wrap(value)\n\n    // If there was no previous value, we'll end up calling the .value on newWrapper, which will evaluate it for the\n    // first (and last) time\n    cache.putIfAbsent(key, newWrapper).getOrElse(newWrapper).value\n  }\n\n  /**\n   * Remove the key and any associated value from the cache. Optionally return any previously associated value.\n   *\n   * @param key\n   * @return\n   */\n  def remove(key: K): Option[V] = {\n    cache.remove(key).map(unwrap)\n  }\n\n  /**\n   * Remove all keys and values from the cache\n   */\n  def clear() {\n    cache.clear()\n  }\n\n  /**\n   * Return how many elements are in the cache\n   *\n   * @return\n   */\n  def size: Int = {\n    cache.size\n  }\n\n  private def wrap[T](value: => T): LazyWrapper[T] = {\n    new LazyWrapper[T](value)\n  }\n\n  private def unwrap[T](lazyWrapper: LazyWrapper[T]): T = {\n    lazyWrapper.value\n  }\n\n  override def toString: String = \"Cache(%s)\".format(cache)\n\n  override def hashCode(): Int = cache.hashCode()\n\n  override def equals(other: Any): Boolean = {\n    Option(other) match {\n      case Some(otherCache: Cache[_, _]) => cache.equals(otherCache.cache)\n      case _ => false\n    }\n  }\n}\n\n/**\n * A wrapper that avoids evaluating the value until explicitly accessed by calling either .value, .equals, .hashCode,\n * or .toString.\n *\n * @param wrapped\n * @tparam T\n */\nclass LazyWrapper[T](wrapped: => T) {\n\n  // Store in a lazy val to make sure the wrapped value is evaluated at most once\n  lazy val value = wrapped\n\n  override def toString: String = \"LazyWrapper(%s)\".format(value)\n\n  override def hashCode(): Int = value.hashCode()\n\n  override def equals(other: Any): Boolean = {\n    Option(other) match {\n      case Some(otherLazy: LazyWrapper[_]) => value.equals(otherLazy.value)\n      case _ => false\n    }\n  }\n}\n\nobject Cache {\n  val DEFAULT_INITIAL_CAPACITY = 16\n  val DEFAULT_CONCURRENCY_LEVEL = 16\n  val DEFAULT_LOAD_FACTOR = 0.75f\n\n  val CONFIG_KEY_INITIAL_CAPACITY = \"initialCapacity\"\n  val CONFIG_KEY_CONCURRENCY_LEVEL = \"concurrencyLevel\"\n  val CONFIG_KEY_LOAD_FACTOR = \"loadFactor\"\n}"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/dedupe/CacheFilter.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport scala.concurrent.ExecutionContext\n\n/**\n * Any time you use the DedupingCache, you must add this CacheFilter to your filter chain. This filter will initialize\n * the cache for each incoming request and cleanup the cache after you're done processing the request. To avoid memory\n * leaks, you want to be sure this filter runs on every single request, so it's a good idea to make it the very first\n * one in the filter chain, so no other filter can bypass it.\n *\n * @param dedupingCache\n * @tparam K\n * @tparam V\n */\nclass CacheFilter[K, V](dedupingCache: DedupingCache[K, V])(implicit ec: ExecutionContext) extends BeforeAndAfterFilter(\n  before = rh => dedupingCache.initCacheForRequest(rh),\n  after = rh => dedupingCache.cleanupCacheForRequest(rh))\n\n"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/dedupe/CacheNotInitializedException.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nclass CacheNotInitializedException(message: String) extends RuntimeException(message)\n"
  },
  {
    "path": "big-pipe/src/main/scala/com/ybrikman/ping/scalaapi/dedupe/DedupingCache.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport play.api.mvc.RequestHeader\n\n/**\n * A cache you can use to de-dupe expensive calculations to ensure that the same calculation happens at most once while\n * processing any incoming request. For example, imagine you get an incoming request to /foo, and to render this page,\n * you have to make a dozen calls to remote services (e.g. a profile service, a search service, etc) using REST over\n * HTTP. You can use the DedupingCache to ensure you don't make the same exact call more than once (e.g. fetch the\n * exact same profile multiple times) by running all the calls through this client. You would use the URL of the REST\n * call as the key and the Future returned from the call as the value:\n *\n * val remoteUrl = \"http://example.com/some/remote/service\"\n * val future: Future[Response] = dedupingCache.get(remoteUrl, WS.url(remoteUrl).get())\n *\n * While processing any incoming request, using the code above ensures that you will not make multiple calls to the\n * exact same remoteUrl; any duplicates will just return a Future object that is already in the cache.\n *\n * You should only use the DedupingCache for data that is safe to cache. For example, HTTP GET calls are usually safe\n * to cache since they should be idempotent, but HTTP POST calls are not safe to cache. Also, you need to add the\n * CacheFilter to your filter chain so that it can clean up the cache after you're done processing an incoming request.\n * Otherwise, you'll have a memory leak.\n *\n * @tparam K The type to use for keys. This type must define an equals and hashCode method. For example, if you're\n *           making REST over HTTP calls, the HTTP URL is a good key.\n * @tparam V The type of value that will be returned. For example, if you're making REST over HTTP calls using Play's\n *           WS library, a Future[Response] might be a good type for the value.\n */\nclass DedupingCache[K, V] {\n  private val cache = new Cache[Long, Cache[K, V]]()\n\n  /**\n   * Get the value for key K from the cache. If the value is not already in teh cache, use the valueIfMissing function\n   * to calculate a value, store it in the cache, and return that value.\n   *\n   * @param key\n   * @param valueIfMissing\n   * @param playRequest\n   * @return\n   */\n  def get(key: K, valueIfMissing: => V)(implicit playRequest: RequestHeader): V = {\n    getCacheForPlayRequest(playRequest).getOrElseUpdate(key, valueIfMissing)\n  }\n\n  /**\n   * Initialize the cache for the given incoming request. Should only be used by the CacheFilter.\n   *\n   * @param playRequest\n   */\n  def initCacheForRequest(playRequest: RequestHeader): Unit = {\n    cache.put(playRequest.id, new Cache[K, V])\n  }\n\n  /**\n   * Cleanup the cache after you're completely done processing an incoming request. This is necessary to prevent memory\n   * leaks. Should only be used by the CacheFilter.\n   *\n   * @param playRequest\n   */\n  def cleanupCacheForRequest(playRequest: RequestHeader): Unit = {\n    cache.remove(playRequest.id)\n  }\n\n  private def getCacheForPlayRequest(playRequest: RequestHeader): Cache[K, V] = {\n    cache.get(playRequest.id).getOrElse(throw new CacheNotInitializedException(\n      s\"No cache found for request with id ${playRequest.id}. \" +\n      s\"Did you add ${classOf[CacheFilter[_, _]].getName} to your filter chain?\"))\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/main/twirl/com/ybrikman/bigpipe/css.scala.html",
    "content": "@(urls: Seq[String])\n\n@for(url <- urls) {\n  <link rel=\"stylesheet\" href=\"@url\"/>\n}"
  },
  {
    "path": "big-pipe/src/main/twirl/com/ybrikman/bigpipe/js.scala.html",
    "content": "@(urls: Seq[String])\n\n@for(url <- urls) {\n  <script src=\"@url\" type=\"text/javascript\"></script>\n}"
  },
  {
    "path": "big-pipe/src/main/twirl/com/ybrikman/bigpipe/pageletClientSide.scala.html",
    "content": "@(content: String, id: String, contentType: com.ybrikman.ping.javaapi.bigpipe.PageletContentType)\n\n@import com.ybrikman.ping.scalaapi.bigpipe.Embed\n\n@defining(s\"$id-$contentType\") { contentId =>\n  <code id=\"@contentId\" class=\"bigpipe-embed-html\"><!--@Embed.escapeForEmbedding(content)--></code>\n\n  <script>(function() { this.BigPipe.onPagelet(\"@id\", \"@contentId\", \"@contentType.name()\"); }.call(this));</script>\n}"
  },
  {
    "path": "big-pipe/src/main/twirl/com/ybrikman/bigpipe/pageletServerSide.scala.html",
    "content": "@(id: String, content: String)\n\n<div id=\"@id\">@Html(content)</div>"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/javaapi/dedupe/TestCacheFilter.scala",
    "content": "package com.ybrikman.ping.javaapi.dedupe\n\nimport java.util.function.Supplier\n\nimport com.ybrikman.ping.scalaapi.dedupe.CacheNotInitializedException\nimport org.specs2.mutable.Specification\nimport play.api.mvc.{Results, Result, RequestHeader}\nimport play.mvc.Http.{RequestBuilder, Context}\nimport play.api.test.Helpers._\nimport play.api.libs.concurrent.Execution.Implicits._\n\nimport scala.concurrent.Future\n\nclass TestCacheFilter extends Specification {\n  \"The Java CacheFilter should\" >> {\n    \"initialize the cache before the filter chain and clean it up after the filter chain\" >> {\n      val fakeContext = new Context(new RequestBuilder)\n      val cache = new DedupingCache[String, String]\n      val filter = new CacheFilter(cache, defaultContext)\n      val expectedResult = \"bar\"\n\n      def next(rh: RequestHeader): Future[Result] = {\n        Future.successful(Results.Ok(cache.get(\"foo\", supplier(expectedResult), fakeContext)))\n      }\n\n      val fakeRequest = fakeContext._requestHeader()\n      val actualResult = contentAsString(filter(next _)(fakeRequest))\n      actualResult mustEqual expectedResult\n\n      // Ensure cache was cleaned up for that request\n      cache.get(\"foo\", supplier(\"shouldNotBeUsed\"), fakeContext) must throwA[CacheNotInitializedException]\n    }\n\n    \"initialize the cache before the filter chain and clean it up after the filter chain even if an exception is thrown\" >> {\n      val fakeContext = new Context(new RequestBuilder)\n      val cache = new DedupingCache[String, String]\n      val filter = new CacheFilter(cache, defaultContext)\n      val expectedResult = \"bar\"\n\n      def next(rh: RequestHeader): Future[Result] = {\n        throw new CacheFilterTestException(cache.get(\"foo\", supplier(expectedResult), fakeContext))\n      }\n\n      val fakeRequest = fakeContext._requestHeader()\n      contentAsString(filter(next _)(fakeRequest)) must throwA[CacheFilterTestException](message = expectedResult)\n\n      // Ensure cache was cleaned up for that request\n      cache.get(\"foo\", supplier(\"shouldNotBeUsed\"), fakeContext) must throwA[CacheNotInitializedException]\n    }\n  }\n\n  private def supplier[A](value: => A): Supplier[A] = {\n    new Supplier[A] {\n      override def get(): A = value\n    }\n  }\n}\n\nclass CacheFilterTestException(message: String) extends RuntimeException(message)"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/javaapi/dedupe/TestDedupingCache.scala",
    "content": "package com.ybrikman.ping.javaapi.dedupe\n\nimport java.util.concurrent.atomic.AtomicInteger\nimport java.util.function.Supplier\n\nimport com.ybrikman.ping.scalaapi.dedupe.CacheNotInitializedException\nimport org.specs2.mutable.Specification\nimport play.mvc.Http.{RequestBuilder, Context}\n\nclass TestDedupingCache extends Specification {\n  \"The Java DedupingCache get method should\" >> {\n\n    \"throw an exception if the cache is not initialized\" >> {\n      val uninitializedCache = new DedupingCache[String, Integer]\n      val fakeContext = new Context(new RequestBuilder)\n      uninitializedCache.get(\"foo\", supplier(1), fakeContext) must throwA[CacheNotInitializedException]\n    }\n\n    \"return valueIfMissing when the cache is empty\" >> {\n      val fakeContext = new Context(new RequestBuilder)\n      val cache = createInitializedCache[String, Int](fakeContext)\n      val value = new AtomicInteger(0)\n\n      cache.get(\"foo\", supplier(value.incrementAndGet()), fakeContext) mustEqual 1\n    }\n\n    \"store different values for different keys\" >> {\n      val fakeContext = new Context(new RequestBuilder)\n      val cache = createInitializedCache[String, Int](fakeContext)\n\n      val value1 = new AtomicInteger(0)\n      cache.get(\"foo\", supplier(value1.incrementAndGet()), fakeContext) mustEqual 1\n\n      val value2 = new AtomicInteger(100)\n      cache.get(\"bar\", supplier(value2.incrementAndGet()), fakeContext) mustEqual 101\n\n      // Recheck to make sure the later calls to get had no effect on the previous ones\n      cache.get(\"foo\", supplier(value1.incrementAndGet()), fakeContext) mustEqual 1\n      cache.get(\"bar\", supplier(value2.incrementAndGet()), fakeContext) mustEqual 101\n    }\n\n    \"store different values for different requests\" >> {\n      val cache = new DedupingCache[String, Int]\n\n      val fakeContext1 = new Context(new RequestBuilder().id(123L))\n      val fakeContext2 = new Context(new RequestBuilder().id(456L))\n\n      cache.initCacheForRequest(fakeContext1)\n      cache.initCacheForRequest(fakeContext2)\n\n      val value1 = new AtomicInteger(0)\n      cache.get(\"foo\", supplier(value1.incrementAndGet()), fakeContext1) mustEqual 1\n\n      val value2 = new AtomicInteger(100)\n      cache.get(\"foo\", supplier(value2.incrementAndGet()), fakeContext2) mustEqual 101\n\n      // Recheck to make sure the later calls to get had no effect on the previous ones\n      cache.get(\"foo\", supplier(value1.incrementAndGet()), fakeContext1) mustEqual 1\n      cache.get(\"foo\", supplier(value2.incrementAndGet()), fakeContext2) mustEqual 101\n    }\n\n\n    \"only call valueIfMissing once, no matter how many times we call get on the same key\" >> {\n      val fakeContext = new Context(new RequestBuilder)\n      val cache = createInitializedCache[String, Int](fakeContext)\n      val value = new AtomicInteger(0)\n\n      cache.get(\"foo\", supplier(value.incrementAndGet()), fakeContext) mustEqual 1\n      cache.get(\"foo\", supplier(value.incrementAndGet()), fakeContext) mustEqual 1\n      cache.get(\"foo\", supplier(value.incrementAndGet()), fakeContext) mustEqual 1\n    }\n\n    \"not call valueIfMissing at all if the key is already in the cache\" >> {\n      val fakeContext = new Context(new RequestBuilder)\n      val cache = createInitializedCache[String, Int](fakeContext)\n\n      val originalValue = new AtomicInteger(0)\n      cache.get(\"foo\", supplier(originalValue.incrementAndGet()), fakeContext) mustEqual 1\n\n      val newValue = new AtomicInteger(100)\n      cache.get(\"foo\", supplier(newValue.incrementAndGet()), fakeContext) mustEqual 1\n    }\n\n    \"throw an exception if the cache has been cleaned up for the current request\" >> {\n      val fakeContext = new Context(new RequestBuilder)\n      val cache = createInitializedCache[String, Int](fakeContext)\n      val value = new AtomicInteger(0)\n\n      cache.get(\"foo\", supplier(value.incrementAndGet()), fakeContext) mustEqual 1\n\n      cache.cleanupCacheForRequest(fakeContext)\n\n      cache.get(\"foo\", supplier(1), fakeContext) must throwA[CacheNotInitializedException]\n    }\n  }\n\n  private def createInitializedCache[K, V](context: Context): DedupingCache[K, V] = {\n    val cache = new DedupingCache[K, V]\n    cache.initCacheForRequest(context)\n    cache\n  }\n\n  private def supplier[A](value: => A): Supplier[A] = {\n    new Supplier[A] {\n      override def get(): A = value\n    }\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/scalaapi/bigpipe/TestBigPipeJavaScript.scala",
    "content": "package com.ybrikman.ping.scalaapi.bigpipe\n\nimport javax.script.{ScriptEngine, ScriptEngineManager}\n\nimport jdk.nashorn.api.scripting.JSObject\nimport org.specs2.mutable.Specification\nimport play.api.libs.json.Json\n\nimport scala.io.Source\n\nclass TestBigPipeJavaScript extends Specification {\n\n  private val BigPipeJsPath = \"public/com/ybrikman/ping/big-pipe.js\"\n\n  \"big-pipe.js should\" >> {\n    \"be able to unescape HTML escaped by Embed.escapeForEmbedding\" >> {\n      val html = \"<h1>Hello World</h1><!-- The dashes in this comment should be escaped -->\"\n      val escapedHtml = Embed.escapeForEmbedding(html).body\n\n      val engine = getJsEngineWithBigPipeLoaded\n      val result = engine.eval(s\"\"\"BigPipe.unescapeForEmbedding('$escapedHtml')\"\"\")\n      result.toString mustEqual html\n    }\n\n    \"be able to unescape JSON escaped by Embed.escapeForEmbedding\" >> {\n      val data = Map(\"foo\" -> \"bar\", \"baz--\" -> \"--blah--\")\n      val json = Json.stringify(Json.toJson(data))\n      val escapedJson = Embed.escapeForEmbedding(json).body\n\n      val engine = getJsEngineWithBigPipeLoaded\n      val result = engine.eval(\n        s\"\"\"\n           |BigPipe.readEmbeddedContentFromDom = function(domId) {\n           |  return '$escapedJson';\n           |};\n           |BigPipe.parseEmbeddedJsonFromDom('foo')\"\"\".stripMargin).asInstanceOf[JSObject]\n\n      result.getMember(\"foo\") mustEqual data(\"foo\")\n      result.getMember(\"baz--\") mustEqual data(\"baz--\")\n    }\n\n    \"ignore null content when unescaping\" >> {\n      val engine = getJsEngineWithBigPipeLoaded\n      engine.eval(\"\"\"BigPipe.unescapeForEmbedding(null)\"\"\") must beNull\n    }\n  }\n\n  private def getJsEngineWithBigPipeLoaded: ScriptEngine = {\n    // Must create the ScriptEngineManager with the class loader or getEngineByName will return null in SBT\n    // See: https://github.com/sbt/sbt/issues/1214#issuecomment-55566056\n    val engine = new ScriptEngineManager(getClass.getClassLoader).getEngineByName(\"nashorn\")\n    engine.eval(getBigPipeJs)\n    engine\n  }\n\n  private def getBigPipeJs: String = {\n    val inputStream = getClass.getClassLoader.getResourceAsStream(BigPipeJsPath)\n    Source.fromInputStream(inputStream).mkString\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/scalaapi/bigpipe/TestEmbed.scala",
    "content": "package com.ybrikman.ping.scalaapi.bigpipe\n\nimport org.specs2.mutable.Specification\nimport play.twirl.api.Html\n\nclass TestEmbed extends Specification {\n\n  \"The Embed.escapeForEmbedding method should\" >> {\n    \"leave content without dashes unchanged\" >> {\n      Embed.escapeForEmbedding(\"foo bar baz\") mustEqual Html(\"foo bar baz\")\n    }\n\n    \"leave content with single dashes unchanged\" >> {\n      Embed.escapeForEmbedding(\"foo-bar-baz\") mustEqual Html(\"foo-bar-baz\")\n    }\n\n    \"escape all double dashes\" >> {\n      Embed.escapeForEmbedding(\"foo--bar--baz\") mustEqual Html(\"foo\\\\u002d\\\\u002dbar\\\\u002d\\\\u002dbaz\")\n    }\n  }\n}\n"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/scalaapi/dedupe/TestBeforeAndAfterFilter.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport java.util.concurrent.CopyOnWriteArrayList\n\nimport org.specs2.mutable.Specification\nimport play.api.libs.iteratee.Enumerator\nimport play.api.mvc.{Result, RequestHeader, Results}\nimport play.api.test.FakeRequest\nimport play.api.test.Helpers._\nimport scala.collection.JavaConverters._\nimport play.api.libs.concurrent.Execution.Implicits._\n\nimport scala.concurrent.Future\n\nclass TestBeforeAndAfterFilter extends Specification {\n  \"BeforeAndAfterFilter should\" >> {\n    \"Call the before function once before the rest of the filter chain and the after function once after the rest of the filter chain\" >> {\n      val events = new CopyOnWriteArrayList[String]()\n      val expectedResult = \"testing\"\n\n      val filter = new BeforeAndAfterFilter(\n        before = rh => events.add(\"before\"),\n        after = rh => events.add(\"after\"))\n\n      def next(rh: RequestHeader): Future[Result] = {\n        events.add(\"next\")\n        Future.successful(Results.Ok(expectedResult))\n      }\n\n      val actualResult = contentAsString(filter(next _)(FakeRequest()))\n\n      actualResult mustEqual expectedResult\n      events.asScala must containTheSameElementsAs(Seq(\"before\", \"next\", \"after\"))\n    }\n\n    \"Call the before and after functions even if the next item in the filter chain throws an exception\" >> {\n      val events = new CopyOnWriteArrayList[String]()\n\n      val filter = new BeforeAndAfterFilter(\n        before = rh => events.add(\"before\"),\n        after = rh => events.add(\"after\"))\n\n      def next(rh: RequestHeader): Future[Result] = {\n        events.add(\"next\")\n        throw new BeforeAndAfterTestException\n      }\n\n      contentAsString(filter(next _)(FakeRequest())) must throwA[BeforeAndAfterTestException]\n      events.asScala must containTheSameElementsAs(Seq(\"before\", \"next\", \"after\"))\n    }\n\n    \"Call the before and after functions even if the next item in the filter chain returns a failed future\" >> {\n      val events = new CopyOnWriteArrayList[String]()\n\n      val filter = new BeforeAndAfterFilter(\n        before = rh => events.add(\"before\"),\n        after = rh => events.add(\"after\"))\n\n      def next(rh: RequestHeader): Future[Result] = {\n        events.add(\"next\")\n        Future.failed(new BeforeAndAfterTestException)\n      }\n\n      contentAsString(filter(next _)(FakeRequest())) must throwA[BeforeAndAfterTestException]\n      events.asScala must containTheSameElementsAs(Seq(\"before\", \"next\", \"after\"))\n    }\n\n    \"Call the before and after functions even if the next item in the filter chain returns an Enumerator that is already done\" >> {\n      val events = new CopyOnWriteArrayList[String]()\n\n      val filter = new BeforeAndAfterFilter(\n        before = rh => events.add(\"before\"),\n        after = rh => events.add(\"after\"))\n\n      def next(rh: RequestHeader): Future[Result] = {\n        events.add(\"next\")\n        Future.successful(Results.Ok.chunked(Enumerator.eof[String]))\n      }\n\n      contentAsString(filter(next _)(FakeRequest())) mustEqual \"\"\n      events.asScala must containTheSameElementsAs(Seq(\"before\", \"next\", \"after\"))\n    }\n\n    \"Call the before and after functions even if the next item in the filter chain returns an Enumerator that is empty\" >> {\n      val events = new CopyOnWriteArrayList[String]()\n\n      val filter = new BeforeAndAfterFilter(\n        before = rh => events.add(\"before\"),\n        after = rh => events.add(\"after\"))\n\n      def next(rh: RequestHeader): Future[Result] = {\n        events.add(\"next\")\n        Future.successful(Results.Ok.chunked(Enumerator.empty[String]))\n      }\n\n      contentAsString(filter(next _)(FakeRequest())) mustEqual \"\"\n      events.asScala must containTheSameElementsAs(Seq(\"before\", \"next\", \"after\"))\n    }\n\n    \"Call the before and after functions even if the next item in the filter chain returns an Enumerator that throws an exception\" >> {\n      val events = new CopyOnWriteArrayList[String]()\n\n      val filter = new BeforeAndAfterFilter(\n        before = rh => events.add(\"before\"),\n        after = rh => events.add(\"after\"))\n\n      def next(rh: RequestHeader): Future[Result] = {\n        events.add(\"next\")\n        Future.successful(Results.Ok.chunked(Enumerator.repeatM[String](throw new BeforeAndAfterTestException)))\n      }\n\n      contentAsString(filter(next _)(FakeRequest())) must throwA[BeforeAndAfterTestException]\n      events.asScala must containTheSameElementsAs(Seq(\"before\", \"next\", \"after\"))\n    }\n\n    \"Call the before and after functions even if the next item in the filter chain returns an Enumerator built from a failed Future\" >> {\n      val events = new CopyOnWriteArrayList[String]()\n\n      val filter = new BeforeAndAfterFilter(\n        before = rh => events.add(\"before\"),\n        after = rh => events.add(\"after\"))\n\n      def next(rh: RequestHeader): Future[Result] = {\n        events.add(\"next\")\n        Future.successful(Results.Ok.chunked(Enumerator.repeatM[String](Future.failed(new BeforeAndAfterTestException))))\n      }\n\n      contentAsString(filter(next _)(FakeRequest())) must throwA[BeforeAndAfterTestException]\n      events.asScala must containTheSameElementsAs(Seq(\"before\", \"next\", \"after\"))\n    }\n\n  }\n}\n\nclass BeforeAndAfterTestException extends RuntimeException"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/scalaapi/dedupe/TestCache.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport java.util.concurrent.atomic.AtomicInteger\n\nimport org.specs2.mutable.Specification\n\nclass TestCache extends Specification {\n\n  \"The Cache get method should\" >> {\n    \"return None on an empty cache\" >> {\n      val cache = new Cache[String, String]()\n      cache.get(\"foo\") must beNone\n    }\n\n    \"return a value that you insert\" >> {\n      val cache = new Cache[String, String]()\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.get(\"foo\") must beSome(\"bar\")\n    }\n\n    \"return None if you insert one value but get a different one\" >> {\n      val cache = new Cache[String, String]()\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.get(\"baz\") must beNone\n    }\n\n    \"return a new value when you overwrite an old one\" >> {\n      val cache = new Cache[String, String]()\n\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.get(\"foo\") must beSome(\"bar\")\n\n      cache.put(\"foo\", \"baz\") must beSome(\"bar\")\n      cache.get(\"foo\") must beSome(\"baz\")\n    }\n  }\n\n  \"The Cache contains method should\" >> {\n    \"return false for any key when the cache is empty\" >> {\n      val cache = new Cache[String, String]()\n      cache.contains(\"foo\") must beFalse\n    }\n\n    \"return true when you lookup a key that was previously inserted\" >> {\n      val cache = new Cache[String, String]()\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.contains(\"foo\") must beTrue\n    }\n\n    \"return false when you lookup a different key than the one that was previously inserted\" >> {\n      val cache = new Cache[String, String]()\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.contains(\"baz\") must beFalse\n    }\n  }\n\n  \"Cache getOrElseUpdate method should\" >> {\n    \"return the new value on an empty cache\" >> {\n      val cache = new Cache[String, String]()\n      cache.getOrElseUpdate(\"foo\", \"bar\") mustEqual \"bar\"\n      cache.get(\"foo\") must beSome(\"bar\")\n    }\n\n    \"return the old value if the same key had already been inserted and not overwrite the old value\" >> {\n      val cache = new Cache[String, String]()\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.getOrElseUpdate(\"foo\", \"baz\") mustEqual \"bar\"\n      cache.get(\"foo\") must beSome(\"bar\")\n    }\n\n    \"evaluate the value exactly once if the key was not already present in the cache\" >> {\n      val cache = new Cache[String, LazyEvalTestClass]()\n\n      // No value previously present, make sure new value gets evaluated correctly\n      val evalCount = new AtomicInteger(0)\n      val result = cache.getOrElseUpdate(\"foo\", LazyEvalTestClass(evalCount, failIfEvaluated = false, uniqueId = 5))\n\n      evalCount.get() mustEqual 1\n      result.uniqueId mustEqual 5\n\n      // Make sure fetching the value later doesn't cause it to be evaluated again\n      val getResult = cache.get(\"foo\")\n\n      getResult.isDefined must beTrue\n      getResult.get.uniqueId mustEqual 5\n      evalCount.get() mustEqual 1\n    }\n\n    \"not evaluate the value at all if the key was already present in the cache\" >> {\n      val cache = new Cache[String, LazyEvalTestClass]()\n\n      val evalCountPrevious = new AtomicInteger(0)\n      cache.put(\"foo\", LazyEvalTestClass(evalCountPrevious, failIfEvaluated = false, uniqueId = 5)) must beNone\n\n      evalCountPrevious.get() mustEqual 1\n\n      // Make sure inserting another value at the same key does not result in the new value being evaluated\n      val evalCountNew = new AtomicInteger(0)\n      val result = cache.getOrElseUpdate(\"foo\", LazyEvalTestClass(evalCountNew, failIfEvaluated = true, uniqueId = 123))\n\n      evalCountNew.get() mustEqual 0\n      result.uniqueId mustEqual 5\n\n      // Make sure calling get has no effect on the new value either\n      val getResult = cache.get(\"foo\")\n\n      getResult.isDefined must beTrue\n      evalCountNew.get() mustEqual 0\n      evalCountPrevious.get() mustEqual 1\n      getResult.get.uniqueId mustEqual 5\n    }\n  }\n\n  \"Cache putIfAbsent method should\" >> {\n    \"insert the value if the key was not already in the cache\" >> {\n      val cache = new Cache[String, String]()\n      cache.putIfAbsent(\"foo\", \"bar\") must beNone\n      cache.get(\"foo\") must beSome(\"bar\")\n    }\n\n    \"not overwrite the previous value if it was already in the cache\" >> {\n      val cache = new Cache[String, String]()\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.putIfAbsent(\"foo\", \"baz\") must beSome(\"bar\")\n      cache.get(\"foo\") must beSome(\"bar\")\n    }\n\n    \"insert the value if the cache contained values for other keys\" >> {\n      val cache = new Cache[String, String]()\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.putIfAbsent(\"bar\", \"baz\") must beNone\n      cache.get(\"foo\") must beSome(\"bar\")\n      cache.get(\"bar\") must beSome(\"baz\")\n    }\n  }\n\n  \"Cache remove method should\" >> {\n    \"return None on an empty cache\" >> {\n      val cache = new Cache[String, String]()\n      cache.remove(\"foo\") must beNone\n    }\n\n    \"remove values that were inserted previously\" >> {\n      val cache = new Cache[String, String]()\n\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.get(\"foo\") must beSome(\"bar\")\n\n      cache.remove(\"foo\") must beSome(\"bar\")\n      cache.get(\"foo\") must beNone\n    }\n\n    \"only remove the requested keys\" >> {\n      val cache = new Cache[String, String]()\n\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.get(\"foo\") must beSome(\"bar\")\n\n      cache.put(\"baz\", \"blah\") must beNone\n      cache.get(\"baz\") must beSome(\"blah\")\n\n      cache.remove(\"foo\") must beSome(\"bar\")\n      cache.get(\"foo\") must beNone\n      cache.get(\"baz\") must beSome(\"blah\")\n    }\n  }\n\n  \"Cache empty method should\" >> {\n    \"leave an empty cache empty\" >> {\n      val cache = new Cache[String, String]()\n      cache.size mustEqual 0\n      cache.clear()\n      cache.size mustEqual 0\n    }\n\n    \"remove all values from the cache\" >> {\n      val cache = new Cache[String, String]()\n\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.get(\"foo\") must beSome(\"bar\")\n\n      cache.put(\"baz\", \"blah\") must beNone\n      cache.get(\"baz\") must beSome(\"blah\")\n\n      cache.clear()\n\n      cache.get(\"foo\") must beNone\n      cache.get(\"baz\") must beNone\n    }\n  }\n\n  \"Cache size method should\" >> {\n    \"return 0 for an empty cache\" >> {\n      val cache = new Cache[String, String]()\n      cache.size mustEqual 0\n    }\n\n    \"return the number of elements inserted into the cache so far\" >> {\n      val cache = new Cache[String, String]()\n      cache.size mustEqual 0\n\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.size mustEqual 1\n\n      cache.put(\"baz\", \"blah\") must beNone\n      cache.size mustEqual 2\n\n      cache.put(\"baz\", \"abcdef\") must beSome(\"blah\")\n      cache.size mustEqual 2\n    }\n  }\n\n  \"The Cache should\" >> {\n    \"behave correctly across many put, get, and remove calls\" >> {\n      val cache = new Cache[Int, Int]()\n      val range = 0 until 1000\n      val rangeWrappedInOptions = range.map(Option.apply)\n\n      range.map(i => cache.put(i, i)) must contain(beNone).forall\n      range.map(i => cache.get(i)) must containTheSameElementsAs(range.map(Option.apply))\n      range.map(i => cache.remove(i)) must containTheSameElementsAs(range.map(Option.apply))\n      range.map(i => cache.get(i)) must contain(beNone).forall\n    }\n\n    \"behave correctly across many getOrElseUpdate calls\" >> {\n      val cache = new Cache[Int, LazyEvalTestClass]()\n      val smallRange = 0 until 500\n\n      // Insert some initial values and make sure they each get evaluated exactly once\n      val evalCountsAfterPut = smallRange.map { i =>\n        val evalCount = new AtomicInteger(0)\n        cache.put(i, LazyEvalTestClass(evalCount, failIfEvaluated = false, uniqueId = i))\n        evalCount.get()\n      }\n      evalCountsAfterPut must contain(1).forall\n\n      val bigRange = 0 until 1000\n      val constant = 123456\n\n      // Now use getOrElseUpdate to insert more values; the first half should already be in the cache and therefore\n      // skipped (so we expect their evalCount to be 0), while the second half should be new entries that get inserted\n      // (so we expect their evalCount to be 1)\n      val idsAndEvalCountsAfterGetOrElseUpdate = bigRange.map { i =>\n        val evalCount = new AtomicInteger(0)\n\n        val shouldBeEvaluated = !smallRange.contains(i)\n        val uniqueId = i + constant\n        val result = cache.getOrElseUpdate(i, LazyEvalTestClass(evalCount, !shouldBeEvaluated, uniqueId))\n\n        (result.uniqueId, evalCount.get())\n      }\n      val expectedIdsAndEvalCountsAfterGetOrElseUpdate = bigRange.map { i =>\n        val shouldBeEvaluated = !smallRange.contains(i)\n        val uniqueId = i + constant\n\n        val expectedId = if (shouldBeEvaluated) uniqueId else i\n        val expectedEvalCount = if (shouldBeEvaluated) 1 else 0\n\n        (expectedId, expectedEvalCount)\n      }\n      idsAndEvalCountsAfterGetOrElseUpdate must containTheSameElementsAs(expectedIdsAndEvalCountsAfterGetOrElseUpdate)\n\n      // One last sanity check: make sure all the values are in the cache and have a count of 1\n      val actualGetResults = bigRange.map(i => cache.get(i).map(_.evalCount.get()))\n\n      actualGetResults must containTheSameElementsAs(bigRange.map(i => Some(1)))\n    }\n  }\n\n  \"Cache getAll method should\" >> {\n    \"return an empty Map for an empty cache\" >> {\n      val cache = new Cache[String, String]()\n      cache.getAll mustEqual Map.empty\n    }\n\n    \"return a Map with the values in the cache\" >> {\n      val cache = new Cache[String, String]()\n\n      cache.put(\"foo\", \"bar\") must beNone\n      cache.put(\"baz\", \"blah\") must beNone\n\n      cache.getAll mustEqual Map(\"foo\"->\"bar\", \"baz\" -> \"blah\")\n    }\n  }\n}\n\n/**\n * Used to test lazy evaluation. This class increments the evalCount whenever the constructor is called. It also fails\n * the test if failIfEvaluated was set to true and the constructor gets called. This can be used to fail a\n * test if some lazy value was evaluated when it should not have been.\n *\n * @param evalCount\n * @param failIfEvaluated\n */\ncase class LazyEvalTestClass(evalCount: AtomicInteger, failIfEvaluated: Boolean, uniqueId: Int) {\n  evalCount.incrementAndGet()\n  require(!failIfEvaluated)\n\n  // Need to override the equals method generated by the case class because the AtomicInteger class does not implement\n  // equals or hashCode: http://stackoverflow.com/questions/7567502/why-are-two-atomicintegers-never-equal\n  override def equals(obj: Any): Boolean = {\n    obj match {\n      case other: LazyEvalTestClass =>\n        evalCount.get() == other.evalCount.get() &&\n        failIfEvaluated == other.failIfEvaluated &&\n        uniqueId == other.uniqueId\n      case _ => false\n    }\n  }\n}\n\n"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/scalaapi/dedupe/TestCacheFilter.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport org.specs2.mutable.Specification\nimport play.api.libs.concurrent.Execution.Implicits._\nimport play.api.mvc.{Results, Result, RequestHeader}\nimport play.api.test.FakeRequest\nimport play.api.test.Helpers._\n\nimport scala.concurrent.Future\n\nclass TestCacheFilter extends Specification {\n  \"The Scala CacheFilter should\" >> {\n    \"initialize the cache before the filter chain and clean it up after the filter chain\" >> {\n      val cache = new DedupingCache[String, String]\n      val filter = new CacheFilter(cache)\n      val expectedResult = \"bar\"\n\n      def next(rh: RequestHeader): Future[Result] = {\n        Future.successful(Results.Ok(cache.get(\"foo\", expectedResult)(rh)))\n      }\n\n      val fakeRequest = FakeRequest()\n      val actualResult = contentAsString(filter(next _)(fakeRequest))\n      actualResult mustEqual expectedResult\n\n      // Ensure cache was cleaned up for that request\n      cache.get(\"foo\", \"shouldNotBeUsed\")(fakeRequest) must throwA[CacheNotInitializedException]\n    }\n\n    \"initialize the cache before the filter chain and clean it up after the filter chain even if an exception is thrown\" >> {\n      val cache = new DedupingCache[String, String]\n      val filter = new CacheFilter(cache)\n      val expectedResult = \"bar\"\n\n      def next(rh: RequestHeader): Future[Result] = {\n        throw new CacheFilterTestException(cache.get(\"foo\", expectedResult)(rh))\n      }\n\n      val fakeRequest = FakeRequest()\n      contentAsString(filter(next _)(fakeRequest)) must throwA[CacheFilterTestException](message = expectedResult)\n\n      // Ensure cache was cleaned up for that request\n      cache.get(\"foo\", \"shouldNotBeUsed\")(fakeRequest) must throwA[CacheNotInitializedException]\n    }\n  }\n}\n\nclass CacheFilterTestException(message: String) extends RuntimeException(message)"
  },
  {
    "path": "big-pipe/src/test/scala/com/ybrikman/ping/scalaapi/dedupe/TestDedupingCache.scala",
    "content": "package com.ybrikman.ping.scalaapi.dedupe\n\nimport java.util.concurrent.atomic.AtomicInteger\n\nimport org.specs2.mutable.Specification\nimport play.api.mvc.RequestHeader\nimport play.api.test.FakeRequest\n\nclass TestDedupingCache extends Specification {\n  \"The Scala DedupingCache get method should\" >> {\n\n    \"throw an exception if the cache is not initialized\" >> {\n      val uninitializedCache = new DedupingCache[String, Integer]\n      uninitializedCache.get(\"foo\", 1)(FakeRequest()) must throwA[CacheNotInitializedException]\n    }\n\n    \"return valueIfMissing when the cache is empty\" >> {\n      implicit val fakeRequest = FakeRequest()\n      val cache = createInitializedCache[String, Int]\n      val value = new AtomicInteger(0)\n\n      cache.get(\"foo\", value.incrementAndGet()) mustEqual 1\n    }\n\n    \"store different values for different keys\" >> {\n      implicit val fakeRequest = FakeRequest()\n      val cache = createInitializedCache[String, Int]\n\n      val value1 = new AtomicInteger(0)\n      cache.get(\"foo\", value1.incrementAndGet()) mustEqual 1\n\n      val value2 = new AtomicInteger(100)\n      cache.get(\"bar\", value2.incrementAndGet()) mustEqual 101\n\n      // Recheck to make sure the later calls to get had no effect on the previous ones\n      cache.get(\"foo\", value1.incrementAndGet()) mustEqual 1\n      cache.get(\"bar\", value2.incrementAndGet()) mustEqual 101\n    }\n\n    \"store different values for different requests\" >> {\n      val cache = new DedupingCache[String, Int]\n\n      val fakeRequest1 = FakeRequest().copy(id = 123)\n      val fakeRequest2 = FakeRequest().copy(id = 456)\n\n      cache.initCacheForRequest(fakeRequest1)\n      cache.initCacheForRequest(fakeRequest2)\n\n      val value1 = new AtomicInteger(0)\n      cache.get(\"foo\", value1.incrementAndGet())(fakeRequest1) mustEqual 1\n\n      val value2 = new AtomicInteger(100)\n      cache.get(\"foo\", value2.incrementAndGet())(fakeRequest2) mustEqual 101\n\n      // Recheck to make sure the later calls to get had no effect on the previous ones\n      cache.get(\"foo\", value1.incrementAndGet())(fakeRequest1) mustEqual 1\n      cache.get(\"foo\", value2.incrementAndGet())(fakeRequest2) mustEqual 101\n    }\n\n\n    \"only call valueIfMissing once, no matter how many times we call get on the same key\" >> {\n      implicit val fakeRequest = FakeRequest()\n      val cache = createInitializedCache[String, Int]\n      val value = new AtomicInteger(0)\n\n      cache.get(\"foo\", value.incrementAndGet()) mustEqual 1\n      cache.get(\"foo\", value.incrementAndGet()) mustEqual 1\n      cache.get(\"foo\", value.incrementAndGet()) mustEqual 1\n    }\n\n    \"not call valueIfMissing at all if the key is already in the cache\" >> {\n      implicit val fakeRequest = FakeRequest()\n      val cache = createInitializedCache[String, Int]\n\n      val originalValue = new AtomicInteger(0)\n      cache.get(\"foo\", originalValue.incrementAndGet()) mustEqual 1\n\n      val newValue = new AtomicInteger(100)\n      cache.get(\"foo\", newValue.incrementAndGet()) mustEqual 1\n    }\n\n    \"throw an exception if the cache has been cleaned up for the current request\" >> {\n      implicit val fakeRequest = FakeRequest()\n      val cache = createInitializedCache[String, Int]\n      val value = new AtomicInteger(0)\n\n      cache.get(\"foo\", value.incrementAndGet()) mustEqual 1\n\n      cache.cleanupCacheForRequest(fakeRequest)\n\n      cache.get(\"foo\", 1) must throwA[CacheNotInitializedException]\n    }\n  }\n\n  private def createInitializedCache[K, V](implicit request: RequestHeader): DedupingCache[K, V] = {\n    val cache = new DedupingCache[K, V]\n    cache.initCacheForRequest(request)\n    cache\n  }\n}\n"
  },
  {
    "path": "build.sbt",
    "content": "import ReleaseTransformations._\n\n// The BigPipe library\nlazy val bigPipe = (project in file(\"big-pipe\"))\n  .settings(bigPipeSettings:_*)\n  .enablePlugins(SbtTwirl)\n\n// Some shared code for the sample apps\nlazy val sampleAppCommon = (project in file(\"sample-app-common\"))\n  .settings(sampleAppCommonSettings:_*)\n  .enablePlugins(SbtTwirl)\n  .dependsOn(bigPipe)\n\n// The Scala sample app\nlazy val sampleAppScala = (project in file(\"sample-app-scala\"))\n  .settings(sampleAppScalaSettings:_*)\n  .enablePlugins(PlayScala)\n  .dependsOn(bigPipe, sampleAppCommon % \"test->test;compile->compile\")\n\n// The Java sample app\nlazy val sampleAppJava = (project in file(\"sample-app-java\"))\n  .settings(sampleAppJavaSettings:_*)\n  .enablePlugins(PlayJava)\n  .dependsOn(bigPipe, sampleAppCommon % \"test->test;compile->compile\")\n\n// The root project\nlazy val root = (project in file(\".\"))\n  .aggregate(bigPipe, sampleAppCommon, sampleAppScala, sampleAppJava)\n  .settings(rootSettings:_*)\n\n// Settings shared by all the projects\nlazy val commonSettings = Seq(\n  organization := \"com.ybrikman.ping\",\n  scalaVersion := \"2.11.6\",\n  scalacOptions += \"-feature\",\n  resolvers += \"scalaz-bintray\" at \"https://dl.bintray.com/scalaz/releases\"\n) ++ publishSettings\n\n// Settings specific to the bigPipe project\nlazy val bigPipeSettings = Seq(\n  name := \"big-pipe\",\n  libraryDependencies ++= Seq(\n    \"com.typesafe.play\" %% \"play\" % play.core.PlayVersion.current,\n    \"com.typesafe.play\" %% \"play-iteratees\" % play.core.PlayVersion.current,\n    specs2 % Test\n  )\n) ++ commonSettings\n\n// Settings specific to the sampleAppCommon project\nlazy val sampleAppCommonSettings = Seq(\n  name := \"sample-app-common\",\n  libraryDependencies ++= Seq(\n    \"com.typesafe.play\" %% \"play\" % play.core.PlayVersion.current,\n    ws % Test,\n    specs2 % Test\n  )\n) ++ commonSettings ++ streamingTemplateSettings\n\n// Settings specific to the sampleAppScala project\nlazy val sampleAppScalaSettings = Seq(\n  name := \"sample-app-scala\",\n  routesGenerator := InjectedRoutesGenerator,\n  libraryDependencies ++= Seq(\n    ws,\n    specs2 % Test\n  ),\n  // These two settings are to ensure the test servers don't use the same port if they happen to run in parallel\n  fork in Test := true,\n  javaOptions in Test += \"-Dtestserver.port=19111\"\n) ++ commonSettings ++ streamingTemplateSettings\n\n// Settings specific to the sampleAppJava project\nlazy val sampleAppJavaSettings = Seq(\n  name := \"sample-app-java\",\n  routesGenerator := InjectedRoutesGenerator,\n  libraryDependencies ++= Seq(\n    javaWs,\n    \"com.typesafe.play\" %% \"play-java\" % play.core.PlayVersion.current,\n    specs2 % Test\n  ),\n  // These two settings are to ensure the test servers don't use the same port if they happen to run in parallel\n  fork in Test := true,\n  javaOptions in Test += \"-Dtestserver.port=19222\"\n) ++ commonSettings ++ streamingTemplateSettings\n\n// Settings specific to the root project\nlazy val rootSettings = Seq(\n  updateVersionNumberInReadme := {\n    val ReadmeFile = \"README.md\"\n    val readmePath = baseDirectory.value / ReadmeFile\n    val readmeText = IO.read(readmePath)\n    val releaseVersion = version.value\n\n    streams.value.log.info(s\"Updating version number in $readmePath to $releaseVersion\")\n    val DependencyRegex = \"\"\"(\"com.ybrikman.ping\" %% \"big-pipe\" % \")(.+?)(\")\"\"\".r\n    val updatedReadmeText = DependencyRegex.replaceAllIn(readmeText, \"$1\" + releaseVersion + \"$3\")\n    IO.write(readmePath, updatedReadmeText)\n\n    val vcs = releaseVcs.value.getOrElse(throw new RuntimeException(\"Could not find a version control system to commit README changes\"))\n    vcs.add(ReadmeFile) !! streams.value.log\n    val status = (vcs.status !!).trim\n    if (status.nonEmpty) {\n      streams.value.log.info(\"Committing changes to $readmePath\")\n      vcs.commit(s\"Updating version number in $ReadmeFile to $releaseVersion\") ! streams.value.log\n    }\n\n    releaseVersion\n  }\n) ++ commonSettings\n\n// You must added these settings to your Play app to be able to use .scala.stream templates for BigPipe-style streaming\nlazy val streamingTemplateSettings = Seq(\n  TwirlKeys.templateFormats ++= Map(\"stream\" -> \"com.ybrikman.ping.scalaapi.bigpipe.HtmlStreamFormat\"),\n  TwirlKeys.templateImports ++= Vector(\"com.ybrikman.ping.scalaapi.bigpipe.HtmlStream\", \"com.ybrikman.ping.scalaapi.bigpipe._\")\n)\n\nlazy val updateVersionNumberInReadme = taskKey[String](\"Updates the version number in the README to the current version\")\n\n// Used to publish the bigPipe project to Sonatype as per http://www.scala-sbt.org/release/docs/Using-Sonatype.html\nlazy val publishSettings = Seq(\n  publishMavenStyle := true,\n  sonatypeProfileName := \"com.ybrikman\",\n  pomIncludeRepository := { _ => false },\n  publishTo := {\n    val nexus = \"https://oss.sonatype.org/\"\n    if (isSnapshot.value)\n      Some(\"snapshots\" at nexus + \"content/repositories/snapshots\")\n    else\n      Some(\"releases\"  at nexus + \"service/local/staging/deploy/maven2\")\n  },\n  licenses := Seq(\"MIT License\" -> url(\"http://www.opensource.org/licenses/mit-license.php\")),\n  homepage := Some(url(\"https://github.com/brikis98/ping-play\")),\n  scmInfo := Some(ScmInfo(url(\"https://github.com/brikis98/ping-play\"), \"scm:git:git@github.com:brikis98/ping-play.git\")),\n  // The \"developers\" key does not get inserted into the POM correctly, so we have to use pomExtra and do it manually\n  // developers := List(Developer(\"brikis98\", \"Yevgeniy Brikman\", \"jim@ybrikman.com\", url(\"http://www.ybrikman.com\"))),\n  pomExtra := (\n    <developers>\n      <developer>\n        <id>brikis98</id>\n        <name>Yevgeniy Brikman</name>\n        <url>http://www.ybrikman.com</url>\n      </developer>\n    </developers>\n    ),\n  releaseProcess := Seq[ReleaseStep](\n    checkSnapshotDependencies,\n    inquireVersions,\n    runClean,\n    runTest,\n    setReleaseVersion,\n    releaseStepTask(updateVersionNumberInReadme),\n    commitReleaseVersion,\n    tagRelease,\n    ReleaseStep(action = Command.process(\"publishSigned\", _)),\n    setNextVersion,\n    commitNextVersion,\n    ReleaseStep(action = Command.process(\"sonatypeReleaseAll\", _)),\n    pushChanges\n  )\n)\n\n\n\n\n\n"
  },
  {
    "path": "circle.yml",
    "content": "machine:\n  services:\n    - docker\n\ndependencies:\n  cache_directories:\n      - \"~/docker-compose-1.2.0\"\n      - \"~/docker-build-cache\"\n  pre:\n    - if [[ ! -e ~/docker-compose-1.2.0 ]]; then mkdir -p ~/docker-compose-1.2.0 && curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose-1.2.0/docker-compose && chmod +x ~/docker-compose-1.2.0/docker-compose; fi\n  override:\n    - if [[ -e ~/docker-build-cache/brikis98-ping-play-image.tar ]]; then docker load -i ~/docker-build-cache/brikis98-ping-play-image.tar; fi\n    - docker build -t brikis98/ping-play .\n    - mkdir -p ~/docker-build-cache && docker save brikis98/ping-play > ~/docker-build-cache/brikis98-ping-play-image.tar\n\ntest:\n  override:\n    - ~/docker-compose-1.2.0/docker-compose run web activator test"
  },
  {
    "path": "docker-compose.yml",
    "content": "web:  \n  image: brikis98/ping-play\n  volumes:\n    - .:/src\n  ports:\n    - \"9000:9000\"\n  stdin_open: true\n"
  },
  {
    "path": "project/build.properties",
    "content": "sbt.version=0.13.8\n"
  },
  {
    "path": "project/plugins.sbt",
    "content": "logLevel := Level.Warn\n\nresolvers += \"Typesafe repository\" at \"http://repo.typesafe.com/typesafe/releases/\"\n\naddSbtPlugin(\"com.typesafe.play\" % \"sbt-plugin\" % \"2.4.0\")\n\naddSbtPlugin(\"com.typesafe.sbt\" % \"sbt-twirl\" % \"1.1.1\")\n\naddSbtPlugin(\"com.github.gseitz\" % \"sbt-release\" % \"1.0.0\")\n\naddSbtPlugin(\"org.xerial.sbt\" % \"sbt-sonatype\" % \"0.5.0\")\n\naddSbtPlugin(\"com.jsuereth\" % \"sbt-pgp\" % \"1.0.0\")"
  },
  {
    "path": "sample-app-common/src/main/resources/public/javascripts/big-pipe-with-mustache.js",
    "content": "(function(window, mustache, BigPipe) {\n  \"use strict\";\n\n  var document = window.document;\n  var console = window.console;\n\n  // In a real app, you'd probably want to store the template in an external file and not inline it like this.\n  var template =\n      '<div class=\"module\">' +\n        '<h3 class=\"id\">{{ id }}</h3>' +\n        '<h6>took</h6>' +\n        '<h2 class=\"highlight\">{{ delay }} ms</h2>' +\n        '<h6>to respond</h6>' +\n       '</div>';\n\n  // Override the original BigPipe.renderPagelet method with one that uses mustache.js for client-side rendering\n  BigPipe.renderPagelet = function(id, json) {\n    var domElement = document.getElementById(id);\n    if (domElement) {\n      domElement.innerHTML = Mustache.render(template, json);\n    } else {\n      console.log(\"ERROR: cannot render pagelet because DOM node with id \" + id + \" does not exist\");\n    }\n  };\n\n})(window, Mustache, BigPipe);"
  },
  {
    "path": "sample-app-common/src/main/resources/public/javascripts/mustache.js",
    "content": "/*!\n * mustache.js - Logic-less {{mustache}} templates with JavaScript\n * http://github.com/janl/mustache.js\n */\n\n/*global define: false Mustache: true*/\n\n(function defineMustache (global, factory) {\n  if (typeof exports === 'object' && exports) {\n    factory(exports); // CommonJS\n  } else if (typeof define === 'function' && define.amd) {\n    define(['exports'], factory); // AMD\n  } else {\n    global.Mustache = {};\n    factory(Mustache); // script, wsh, asp\n  }\n}(this, function mustacheFactory (mustache) {\n\n  var objectToString = Object.prototype.toString;\n  var isArray = Array.isArray || function isArrayPolyfill (object) {\n    return objectToString.call(object) === '[object Array]';\n  };\n\n  function isFunction (object) {\n    return typeof object === 'function';\n  }\n\n  function escapeRegExp (string) {\n    return string.replace(/[\\-\\[\\]{}()*+?.,\\\\\\^$|#\\s]/g, '\\\\$&');\n  }\n\n  /**\n   * Null safe way of checking whether or not an object,\n   * including its prototype, has a given property\n   */\n  function hasProperty (obj, propName) {\n    return obj != null && typeof obj === 'object' && (propName in obj);\n  }\n\n  // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577\n  // See https://github.com/janl/mustache.js/issues/189\n  var regExpTest = RegExp.prototype.test;\n  function testRegExp (re, string) {\n    return regExpTest.call(re, string);\n  }\n\n  var nonSpaceRe = /\\S/;\n  function isWhitespace (string) {\n    return !testRegExp(nonSpaceRe, string);\n  }\n\n  var entityMap = {\n    '&': '&amp;',\n    '<': '&lt;',\n    '>': '&gt;',\n    '\"': '&quot;',\n    \"'\": '&#39;',\n    '/': '&#x2F;'\n  };\n\n  function escapeHtml (string) {\n    return String(string).replace(/[&<>\"'\\/]/g, function fromEntityMap (s) {\n      return entityMap[s];\n    });\n  }\n\n  var whiteRe = /\\s*/;\n  var spaceRe = /\\s+/;\n  var equalsRe = /\\s*=/;\n  var curlyRe = /\\s*\\}/;\n  var tagRe = /#|\\^|\\/|>|\\{|&|=|!/;\n\n  /**\n   * Breaks up the given `template` string into a tree of tokens. If the `tags`\n   * argument is given here it must be an array with two string values: the\n   * opening and closing tags used in the template (e.g. [ \"<%\", \"%>\" ]). Of\n   * course, the default is to use mustaches (i.e. mustache.tags).\n   *\n   * A token is an array with at least 4 elements. The first element is the\n   * mustache symbol that was used inside the tag, e.g. \"#\" or \"&\". If the tag\n   * did not contain a symbol (i.e. {{myValue}}) this element is \"name\". For\n   * all text that appears outside a symbol this element is \"text\".\n   *\n   * The second element of a token is its \"value\". For mustache tags this is\n   * whatever else was inside the tag besides the opening symbol. For text tokens\n   * this is the text itself.\n   *\n   * The third and fourth elements of the token are the start and end indices,\n   * respectively, of the token in the original template.\n   *\n   * Tokens that are the root node of a subtree contain two more elements: 1) an\n   * array of tokens in the subtree and 2) the index in the original template at\n   * which the closing tag for that section begins.\n   */\n  function parseTemplate (template, tags) {\n    if (!template)\n      return [];\n\n    var sections = [];     // Stack to hold section tokens\n    var tokens = [];       // Buffer to hold the tokens\n    var spaces = [];       // Indices of whitespace tokens on the current line\n    var hasTag = false;    // Is there a {{tag}} on the current line?\n    var nonSpace = false;  // Is there a non-space char on the current line?\n\n    // Strips all whitespace tokens array for the current line\n    // if there was a {{#tag}} on it and otherwise only space.\n    function stripSpace () {\n      if (hasTag && !nonSpace) {\n        while (spaces.length)\n          delete tokens[spaces.pop()];\n      } else {\n        spaces = [];\n      }\n\n      hasTag = false;\n      nonSpace = false;\n    }\n\n    var openingTagRe, closingTagRe, closingCurlyRe;\n    function compileTags (tagsToCompile) {\n      if (typeof tagsToCompile === 'string')\n        tagsToCompile = tagsToCompile.split(spaceRe, 2);\n\n      if (!isArray(tagsToCompile) || tagsToCompile.length !== 2)\n        throw new Error('Invalid tags: ' + tagsToCompile);\n\n      openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\\\s*');\n      closingTagRe = new RegExp('\\\\s*' + escapeRegExp(tagsToCompile[1]));\n      closingCurlyRe = new RegExp('\\\\s*' + escapeRegExp('}' + tagsToCompile[1]));\n    }\n\n    compileTags(tags || mustache.tags);\n\n    var scanner = new Scanner(template);\n\n    var start, type, value, chr, token, openSection;\n    while (!scanner.eos()) {\n      start = scanner.pos;\n\n      // Match any text between tags.\n      value = scanner.scanUntil(openingTagRe);\n\n      if (value) {\n        for (var i = 0, valueLength = value.length; i < valueLength; ++i) {\n          chr = value.charAt(i);\n\n          if (isWhitespace(chr)) {\n            spaces.push(tokens.length);\n          } else {\n            nonSpace = true;\n          }\n\n          tokens.push([ 'text', chr, start, start + 1 ]);\n          start += 1;\n\n          // Check for whitespace on the current line.\n          if (chr === '\\n')\n            stripSpace();\n        }\n      }\n\n      // Match the opening tag.\n      if (!scanner.scan(openingTagRe))\n        break;\n\n      hasTag = true;\n\n      // Get the tag type.\n      type = scanner.scan(tagRe) || 'name';\n      scanner.scan(whiteRe);\n\n      // Get the tag value.\n      if (type === '=') {\n        value = scanner.scanUntil(equalsRe);\n        scanner.scan(equalsRe);\n        scanner.scanUntil(closingTagRe);\n      } else if (type === '{') {\n        value = scanner.scanUntil(closingCurlyRe);\n        scanner.scan(curlyRe);\n        scanner.scanUntil(closingTagRe);\n        type = '&';\n      } else {\n        value = scanner.scanUntil(closingTagRe);\n      }\n\n      // Match the closing tag.\n      if (!scanner.scan(closingTagRe))\n        throw new Error('Unclosed tag at ' + scanner.pos);\n\n      token = [ type, value, start, scanner.pos ];\n      tokens.push(token);\n\n      if (type === '#' || type === '^') {\n        sections.push(token);\n      } else if (type === '/') {\n        // Check section nesting.\n        openSection = sections.pop();\n\n        if (!openSection)\n          throw new Error('Unopened section \"' + value + '\" at ' + start);\n\n        if (openSection[1] !== value)\n          throw new Error('Unclosed section \"' + openSection[1] + '\" at ' + start);\n      } else if (type === 'name' || type === '{' || type === '&') {\n        nonSpace = true;\n      } else if (type === '=') {\n        // Set the tags for the next time around.\n        compileTags(value);\n      }\n    }\n\n    // Make sure there are no open sections when we're done.\n    openSection = sections.pop();\n\n    if (openSection)\n      throw new Error('Unclosed section \"' + openSection[1] + '\" at ' + scanner.pos);\n\n    return nestTokens(squashTokens(tokens));\n  }\n\n  /**\n   * Combines the values of consecutive text tokens in the given `tokens` array\n   * to a single token.\n   */\n  function squashTokens (tokens) {\n    var squashedTokens = [];\n\n    var token, lastToken;\n    for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {\n      token = tokens[i];\n\n      if (token) {\n        if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {\n          lastToken[1] += token[1];\n          lastToken[3] = token[3];\n        } else {\n          squashedTokens.push(token);\n          lastToken = token;\n        }\n      }\n    }\n\n    return squashedTokens;\n  }\n\n  /**\n   * Forms the given array of `tokens` into a nested tree structure where\n   * tokens that represent a section have two additional items: 1) an array of\n   * all tokens that appear in that section and 2) the index in the original\n   * template that represents the end of that section.\n   */\n  function nestTokens (tokens) {\n    var nestedTokens = [];\n    var collector = nestedTokens;\n    var sections = [];\n\n    var token, section;\n    for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {\n      token = tokens[i];\n\n      switch (token[0]) {\n      case '#':\n      case '^':\n        collector.push(token);\n        sections.push(token);\n        collector = token[4] = [];\n        break;\n      case '/':\n        section = sections.pop();\n        section[5] = token[2];\n        collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;\n        break;\n      default:\n        collector.push(token);\n      }\n    }\n\n    return nestedTokens;\n  }\n\n  /**\n   * A simple string scanner that is used by the template parser to find\n   * tokens in template strings.\n   */\n  function Scanner (string) {\n    this.string = string;\n    this.tail = string;\n    this.pos = 0;\n  }\n\n  /**\n   * Returns `true` if the tail is empty (end of string).\n   */\n  Scanner.prototype.eos = function eos () {\n    return this.tail === '';\n  };\n\n  /**\n   * Tries to match the given regular expression at the current position.\n   * Returns the matched text if it can match, the empty string otherwise.\n   */\n  Scanner.prototype.scan = function scan (re) {\n    var match = this.tail.match(re);\n\n    if (!match || match.index !== 0)\n      return '';\n\n    var string = match[0];\n\n    this.tail = this.tail.substring(string.length);\n    this.pos += string.length;\n\n    return string;\n  };\n\n  /**\n   * Skips all text until the given regular expression can be matched. Returns\n   * the skipped string, which is the entire tail if no match can be made.\n   */\n  Scanner.prototype.scanUntil = function scanUntil (re) {\n    var index = this.tail.search(re), match;\n\n    switch (index) {\n    case -1:\n      match = this.tail;\n      this.tail = '';\n      break;\n    case 0:\n      match = '';\n      break;\n    default:\n      match = this.tail.substring(0, index);\n      this.tail = this.tail.substring(index);\n    }\n\n    this.pos += match.length;\n\n    return match;\n  };\n\n  /**\n   * Represents a rendering context by wrapping a view object and\n   * maintaining a reference to the parent context.\n   */\n  function Context (view, parentContext) {\n    this.view = view;\n    this.cache = { '.': this.view };\n    this.parent = parentContext;\n  }\n\n  /**\n   * Creates a new context using the given view with this context\n   * as the parent.\n   */\n  Context.prototype.push = function push (view) {\n    return new Context(view, this);\n  };\n\n  /**\n   * Returns the value of the given name in this context, traversing\n   * up the context hierarchy if the value is absent in this context's view.\n   */\n  Context.prototype.lookup = function lookup (name) {\n    var cache = this.cache;\n\n    var value;\n    if (cache.hasOwnProperty(name)) {\n      value = cache[name];\n    } else {\n      var context = this, names, index, lookupHit = false;\n\n      while (context) {\n        if (name.indexOf('.') > 0) {\n          value = context.view;\n          names = name.split('.');\n          index = 0;\n\n          /**\n           * Using the dot notion path in `name`, we descend through the\n           * nested objects.\n           *\n           * To be certain that the lookup has been successful, we have to\n           * check if the last object in the path actually has the property\n           * we are looking for. We store the result in `lookupHit`.\n           *\n           * This is specially necessary for when the value has been set to\n           * `undefined` and we want to avoid looking up parent contexts.\n           **/\n          while (value != null && index < names.length) {\n            if (index === names.length - 1)\n              lookupHit = hasProperty(value, names[index]);\n\n            value = value[names[index++]];\n          }\n        } else {\n          value = context.view[name];\n          lookupHit = hasProperty(context.view, name);\n        }\n\n        if (lookupHit)\n          break;\n\n        context = context.parent;\n      }\n\n      cache[name] = value;\n    }\n\n    if (isFunction(value))\n      value = value.call(this.view);\n\n    return value;\n  };\n\n  /**\n   * A Writer knows how to take a stream of tokens and render them to a\n   * string, given a context. It also maintains a cache of templates to\n   * avoid the need to parse the same template twice.\n   */\n  function Writer () {\n    this.cache = {};\n  }\n\n  /**\n   * Clears all cached templates in this writer.\n   */\n  Writer.prototype.clearCache = function clearCache () {\n    this.cache = {};\n  };\n\n  /**\n   * Parses and caches the given `template` and returns the array of tokens\n   * that is generated from the parse.\n   */\n  Writer.prototype.parse = function parse (template, tags) {\n    var cache = this.cache;\n    var tokens = cache[template];\n\n    if (tokens == null)\n      tokens = cache[template] = parseTemplate(template, tags);\n\n    return tokens;\n  };\n\n  /**\n   * High-level method that is used to render the given `template` with\n   * the given `view`.\n   *\n   * The optional `partials` argument may be an object that contains the\n   * names and templates of partials that are used in the template. It may\n   * also be a function that is used to load partial templates on the fly\n   * that takes a single argument: the name of the partial.\n   */\n  Writer.prototype.render = function render (template, view, partials) {\n    var tokens = this.parse(template);\n    var context = (view instanceof Context) ? view : new Context(view);\n    return this.renderTokens(tokens, context, partials, template);\n  };\n\n  /**\n   * Low-level method that renders the given array of `tokens` using\n   * the given `context` and `partials`.\n   *\n   * Note: The `originalTemplate` is only ever used to extract the portion\n   * of the original template that was contained in a higher-order section.\n   * If the template doesn't use higher-order sections, this argument may\n   * be omitted.\n   */\n  Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate) {\n    var buffer = '';\n\n    var token, symbol, value;\n    for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {\n      value = undefined;\n      token = tokens[i];\n      symbol = token[0];\n\n      if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate);\n      else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate);\n      else if (symbol === '>') value = this.renderPartial(token, context, partials, originalTemplate);\n      else if (symbol === '&') value = this.unescapedValue(token, context);\n      else if (symbol === 'name') value = this.escapedValue(token, context);\n      else if (symbol === 'text') value = this.rawValue(token);\n\n      if (value !== undefined)\n        buffer += value;\n    }\n\n    return buffer;\n  };\n\n  Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) {\n    var self = this;\n    var buffer = '';\n    var value = context.lookup(token[1]);\n\n    // This function is used to render an arbitrary template\n    // in the current context by higher-order sections.\n    function subRender (template) {\n      return self.render(template, context, partials);\n    }\n\n    if (!value) return;\n\n    if (isArray(value)) {\n      for (var j = 0, valueLength = value.length; j < valueLength; ++j) {\n        buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);\n      }\n    } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {\n      buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);\n    } else if (isFunction(value)) {\n      if (typeof originalTemplate !== 'string')\n        throw new Error('Cannot use higher-order sections without the original template');\n\n      // Extract the portion of the original template that the section contains.\n      value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);\n\n      if (value != null)\n        buffer += value;\n    } else {\n      buffer += this.renderTokens(token[4], context, partials, originalTemplate);\n    }\n    return buffer;\n  };\n\n  Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) {\n    var value = context.lookup(token[1]);\n\n    // Use JavaScript's definition of falsy. Include empty arrays.\n    // See https://github.com/janl/mustache.js/issues/186\n    if (!value || (isArray(value) && value.length === 0))\n      return this.renderTokens(token[4], context, partials, originalTemplate);\n  };\n\n  Writer.prototype.renderPartial = function renderPartial (token, context, partials) {\n    if (!partials) return;\n\n    var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];\n    if (value != null)\n      return this.renderTokens(this.parse(value), context, partials, value);\n  };\n\n  Writer.prototype.unescapedValue = function unescapedValue (token, context) {\n    var value = context.lookup(token[1]);\n    if (value != null)\n      return value;\n  };\n\n  Writer.prototype.escapedValue = function escapedValue (token, context) {\n    var value = context.lookup(token[1]);\n    if (value != null)\n      return mustache.escape(value);\n  };\n\n  Writer.prototype.rawValue = function rawValue (token) {\n    return token[1];\n  };\n\n  mustache.name = 'mustache.js';\n  mustache.version = '2.1.2';\n  mustache.tags = [ '{{', '}}' ];\n\n  // All high-level mustache.* functions use this writer.\n  var defaultWriter = new Writer();\n\n  /**\n   * Clears all cached templates in the default writer.\n   */\n  mustache.clearCache = function clearCache () {\n    return defaultWriter.clearCache();\n  };\n\n  /**\n   * Parses and caches the given template in the default writer and returns the\n   * array of tokens it contains. Doing this ahead of time avoids the need to\n   * parse templates on the fly as they are rendered.\n   */\n  mustache.parse = function parse (template, tags) {\n    return defaultWriter.parse(template, tags);\n  };\n\n  /**\n   * Renders the `template` with the given `view` and `partials` using the\n   * default writer.\n   */\n  mustache.render = function render (template, view, partials) {\n    return defaultWriter.render(template, view, partials);\n  };\n\n  // This is here for backwards compatibility with 0.4.x.,\n  /*eslint-disable */ // eslint wants camel cased function name\n  mustache.to_html = function to_html (template, view, partials, send) {\n    /*eslint-enable*/\n\n    var result = mustache.render(template, view, partials);\n\n    if (isFunction(send)) {\n      send(result);\n    } else {\n      return result;\n    }\n  };\n\n  // Export the escaping function so that the user may override it.\n  // See https://github.com/janl/mustache.js/issues/244\n  mustache.escape = escapeHtml;\n\n  // Export these mainly for testing, but also for advanced usage.\n  mustache.Scanner = Scanner;\n  mustache.Context = Context;\n  mustache.Writer = Writer;\n\n}));\n"
  },
  {
    "path": "sample-app-common/src/main/resources/public/javascripts/timing.js",
    "content": "// Quick, hacky code used to display page load timing using the Navigation Timing API\n(function(window) {\n  \"use strict\";\n\n  var document = window.document;\n\n  if (window.performance && window.performance.timing) {\n    var timing = window.performance.timing;\n\n    var timeToFirstByte = timing.responseStart - timing.requestStart;\n    var timeToDomLoading = timing.domLoading - timing.requestStart;\n\n    document.getElementById(\"time-to-first-byte\").innerHTML = timeToFirstByte + \"ms\";\n    document.getElementById(\"time-to-dom-loading\").innerHTML = timeToDomLoading + \"ms\";\n  } else {\n    document.getElementById(\"time-to-first-byte\").innerHTML = \"Navigation Timing API not supported in this browser\"\n    document.getElementById(\"time-to-dom-loading\").innerHTML = \"Navigation Timing API not supported in this browser\"\n  }\n})(window);"
  },
  {
    "path": "sample-app-common/src/main/resources/public/stylesheets/main.css",
    "content": "body {\n  padding: 20px;\n}\n\n.wrapper td {\n  width: 200px;\n  height: 160px;\n  border: 1px solid #CCC;\n  border-radius: 5px;\n  text-align: center;\n  margin: 20px;\n}\n\n#timing {\n  margin: 20px 0;\n}\n\n#timing td {\n  padding-right: 10px;\n}\n\nh1, h2, h3, h4, h5, h6 {\n  font-family: \"Helvetica Neue\", Helvetica, Arial;\n  margin: 0;\n}\n\nh1 {\n  font-size: 48px;\n}\n\nh2 {\n  font-size: 36px;\n}\n\nh3 {\n  font-size: 24px;\n}\n\nh4 {\n  font-size: 18px;\n}\n\nh5 {\n  font-size: 14px;\n}\n\nh6 {\n  font-size: 12px;\n}\n\n.highlight {\n  color: #0077b5;\n}\n"
  },
  {
    "path": "sample-app-common/src/main/scala/data/FakeServiceClient.scala",
    "content": "package data\n\nimport play.api.libs.concurrent.Execution.Implicits._\nimport play.api.libs.json.{Json, JsValue}\n\nimport scala.concurrent.Future\nimport scala.util.Random\nimport data.Response._\n\n/**\n * A client that represents fake calls to remote backend services.\n */\nclass FakeServiceClient(futureUtil: FutureUtil) {\n\n  import data.FakeServiceClient._\n\n  def fakeRemoteCallFast(id: String): Future[Response] = fakeRemoteCall(id, FAST_RESPONSE_TIME_IN_MILLIS)\n  def fakeRemoteCallMedium(id: String): Future[Response] = fakeRemoteCall(id, MEDIUM_RESPONSE_TIME_IN_MILLIS)\n  def fakeRemoteCallSlow(id: String): Future[Response] = fakeRemoteCall(id, SLOW_RESPONSE_TIME_IN_MILLIS)\n\n  def fakeRemoteCallJsonFast(id: String): Future[JsValue] = fakeRemoteCallJson(id, FAST_RESPONSE_TIME_IN_MILLIS)\n  def fakeRemoteCallJsonMedium(id: String): Future[JsValue] = fakeRemoteCallJson(id, MEDIUM_RESPONSE_TIME_IN_MILLIS)\n  def fakeRemoteCallJsonSlow(id: String): Future[JsValue] = fakeRemoteCallJson(id, SLOW_RESPONSE_TIME_IN_MILLIS)\n  def fakeRemoteCallJson(id: String, delayInMillis: Long): Future[JsValue] = fakeRemoteCall(id, delayInMillis).map(Json.toJson(_))\n\n  def fakeRemoteCallErrorFast(id: String): Future[Response] = fakeRemoteCallError(id, FAST_RESPONSE_TIME_IN_MILLIS)\n  def fakeRemoteCallErrorMedium(id: String): Future[Response] = fakeRemoteCallError(id, MEDIUM_RESPONSE_TIME_IN_MILLIS)\n  def fakeRemoteCallErrorSlow(id: String): Future[Response] = fakeRemoteCallError(id, SLOW_RESPONSE_TIME_IN_MILLIS)\n\n  def fakeRemoteCallError(id: String, delayInMillis: Long): Future[Response] = {\n    fakeRemoteCall(id, delayInMillis).map(response => throw FakeRemoteCallException(response))\n  }\n\n  def fakeRemoteCall(id: String, delayInMillis: Long): Future[Response] = {\n    val randomJitter = new Random().nextInt(delayInMillis.toInt).toLong\n    val delay = delayInMillis + randomJitter\n\n    val fakeJsonResponse = Response(id, delay)\n    futureUtil.timeout(fakeJsonResponse, delay)\n  }\n}\n\nobject FakeServiceClient {\n  val FAST_RESPONSE_TIME_IN_MILLIS = 5\n  val MEDIUM_RESPONSE_TIME_IN_MILLIS = 500\n  val SLOW_RESPONSE_TIME_IN_MILLIS = 3000\n\n  val RESPONSE_TO_TEST_ESCAPING = \"Escaping test <!-- This comment should be escaped in the HTML -->\"\n}\n\ncase class FakeRemoteCallException(response: Response) extends RuntimeException(s\"\"\"Error in \"${response.id}\" after ${response.delay} ms\"\"\")\n\n\n"
  },
  {
    "path": "sample-app-common/src/main/scala/data/FutureUtil.scala",
    "content": "package data\n\nimport java.util.concurrent.TimeUnit\nimport java.util.function.Supplier\n\nimport akka.actor.ActorSystem\nimport play.libs.F.Promise\nimport play.libs.HttpExecution\n\nimport scala.concurrent.{ExecutionContext, Future}\nimport scala.concurrent.duration.FiniteDuration\nimport akka.pattern.after\n\nclass FutureUtil(actorSystem: ActorSystem) {\n\n  /**\n   * Return a Scala Future that will be redeemed with the given message after the specified delay.\n   *\n   * @param message\n   * @param delay\n   * @param unit\n   * @param ec\n   * @tparam A\n   * @return\n   */\n  def timeout[A](message: => A, delay: Long, unit: TimeUnit = TimeUnit.MILLISECONDS)(implicit ec: ExecutionContext): Future[A] = {\n    after(FiniteDuration(delay, TimeUnit.MILLISECONDS), actorSystem.scheduler)(Future(message))\n  }\n\n  /**\n   * Return a Java Promise that will be redeemed with the given message after the specified delay.\n   *\n   * @param message\n   * @param delay\n   * @param unit\n   * @tparam A\n   * @return\n   */\n  def timeout[A](message: Supplier[A], delay: Long, unit: TimeUnit): Promise[A] = {\n    timeout(message, delay, unit, HttpExecution.defaultContext())\n  }\n\n  /**\n   * Return a Java Promise that will be redeemed with the given message after the specified delay.\n   *\n   * @param message\n   * @param delay\n   * @param unit\n   * @param ec\n   * @tparam A\n   * @return\n   */\n  def timeout[A](message: Supplier[A], delay: Long, unit: TimeUnit, ec: ExecutionContext): Promise[A] = {\n    Promise.wrap(timeout(message.get(), delay)(ec))\n  }\n}\n"
  },
  {
    "path": "sample-app-common/src/main/scala/data/Response.scala",
    "content": "package data\n\nimport play.api.libs.json.Json\n\n/**\n * Simple class used to represent a response from a remote service\n *\n * @param id\n * @param delay\n */\ncase class Response(id: String, delay: Long)\n\nobject Response {\n  implicit val responseWrites = Json.writes[Response]\n}"
  },
  {
    "path": "sample-app-common/src/main/scala/data/UrlAndId.scala",
    "content": "package data\n\n/**\n * Silly container class to pass around the URL and ID\n *\n * @param url\n * @param id\n */\ncase class UrlAndId(url: String, id: String)\n"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/clientSideTemplating.scala.stream",
    "content": "@(bigPipe: BigPipe, profile: Pagelet, graph: Pagelet, feed: Pagelet, inbox: Pagelet, ads: Pagelet, search: Pagelet)\n\n<html>\n  <head>\n    <link rel=\"stylesheet\" href=\"/assets/stylesheets/main.css\">\n    <!-- You need to include the BigPipe JavaScript at the top of the page -->\n    <script src=\"/assets/com/ybrikman/ping/big-pipe.js\"></script>\n    <!-- Include mustache.js, a client-side templating library -->\n    <script src=\"/assets/javascripts/mustache.js\"></script>\n    <!-- Include custom code that will allow you to use BigPipe with mustache.js -->\n    <script src=\"/assets/javascripts/big-pipe-with-mustache.js\"></script>\n  </head>\n  <body>\n    <h1>With Big Pipe and Client-Side Templating</h1>\n    @HtmlStream.fromHtml(views.html.helpers.timing())\n\n    @bigPipe.render { pagelets =>\n      <table class=\"wrapper\">\n        <tr>\n          <td>@pagelets(profile.id)</td>\n          <td>@pagelets(ads.id)</td>\n          <td>@pagelets(feed.id)</td>\n        </tr>\n        <tr>\n          <td>@pagelets(search.id)</td>\n          <td>@pagelets(inbox.id)</td>\n          <td>@pagelets(graph.id)</td>\n        </tr>\n      </table>\n    }\n\n  </body>\n</html>"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/dedupe.scala.html",
    "content": "@(result1: data.UrlAndId, result2: data.UrlAndId, result3: data.UrlAndId, result4: data.UrlAndId)\n\n<html>\n  <head>\n    <style>\n      td { border: 1px solid #CCC; padding: 10px 20px; }\n      .url { width: 50%; }\n      .id { width: 50%; text-align: center; }\n    </style>\n  </head>\n  <body>\n    <h1>Deduping example</h1>\n    <p>\n      Requests to the same remote URL should be de-duped, so only one request is actually sent, and all the others get\n      the same cached response.\n    </p>\n    <table>\n      <thead>\n        <tr>\n          <th>Service URL</th>\n          <th>Request ID</th>\n        </tr>\n      </thead>\n      <tbody>\n        <tr>\n          <td class=\"url\">@result1.url</td>\n          <td class=\"id\">@result1.id</td>\n        </tr>\n        <tr>\n          <td class=\"url\">@result2.url</td>\n          <td class=\"id\">@result2.id</td>\n        </tr>\n        <tr>\n          <td class=\"url\">@result3.url</td>\n          <td class=\"id\">@result3.id</td>\n        </tr>\n        <tr>\n          <td class=\"url\">@result4.url</td>\n          <td class=\"id\">@result4.id</td>\n        </tr>\n      </tbody>\n    </table>\n  </body>\n</html>"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/escaping.scala.stream",
    "content": "@(bigPipe: BigPipe, shouldBeEscaped: Pagelet)\n\n<html>\n  <head>\n    <link rel=\"stylesheet\" href=\"/assets/stylesheets/main.css\">\n    <!-- You need to include the BigPipe JavaScript at the top of the page -->\n    <script src=\"/assets/com/ybrikman/ping/big-pipe.js\"></script>\n    <!-- Include mustache.js, a client-side templating library -->\n    <script src=\"/assets/javascripts/mustache.js\"></script>\n    <!-- Include custom code that will allow you to use BigPipe with mustache.js -->\n    <script src=\"/assets/javascripts/big-pipe-with-mustache.js\"></script>\n  </head>\n  <body>\n    <h1>Big Pipe Escaping Test</h1>\n\n    @bigPipe.render { pagelets =>\n      @pagelets(shouldBeEscaped.id)\n    }\n\n    </body>\n</html>"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/helpers/error.scala.html",
    "content": "@(error: Throwable)\n\n<div class=\"module error\">\n  <h5>There was an</h5>\n  <h2 class=\"highlight id\">error</h2>\n  <h5>fetching data for this pagelet</h5>\n  <h6>@error</h6>\n</div>\n"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/helpers/module.scala.html",
    "content": "@(response: data.Response)\n\n<div class=\"module\">\n  <h3 class=\"id\">@response.id</h3>\n  <h6>took</h6>\n  <h2 class=\"highlight\">@response.delay ms</h2>\n  <h6>to respond</h6>\n</div>\n\n"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/helpers/timing.scala.html",
    "content": "<!-- This code is included only to display timings -->\n<table id=\"timing\">\n  <tr>\n    <td><h4>Time to first byte:</h4></td>\n    <td><h3 id=\"time-to-first-byte\" class=\"highlight\"></h3></td>\n  </tr>\n  <tr>\n    <td><h4>Time to DOM loading:</h4></td>\n    <td><h3 id=\"time-to-dom-loading\" class=\"highlight\"></h3></td>\n  </tr>\n</table>\n<script src=\"/assets/javascripts/timing.js\"></script>\n\n"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/withBigPipe.scala.stream",
    "content": "@(bigPipe: BigPipe, profile: Pagelet, graph: Pagelet, feed: Pagelet, inbox: Pagelet, ads: Pagelet, search: Pagelet)\n\n<html>\n  <head>\n    <link rel=\"stylesheet\" href=\"/assets/stylesheets/main.css\">\n    <!-- You need to include the BigPipe JavaScript at the top of the page -->\n    <script src=\"/assets/com/ybrikman/ping/big-pipe.js\"></script>\n  </head>\n  <body>\n    <h1>With Big Pipe</h1>\n    @HtmlStream.fromHtml(views.html.helpers.timing())\n\n    <!--\n      Wrap the entire body of your page with a bigPipe.render call. The pagelets parameter contains a Map from\n      Pagelet id to the HtmlStream for that Pagelet. You should put the HtmlStream for each of your Pagelets\n      into the appropriate place in the markup.\n    -->\n    @bigPipe.render { pagelets =>\n      <table class=\"wrapper\">\n        <tr>\n          <td>@pagelets(profile.id)</td>\n          <td>@pagelets(ads.id)</td>\n          <td>@pagelets(feed.id)</td>\n        </tr>\n        <tr>\n          <td>@pagelets(search.id)</td>\n          <td>@pagelets(inbox.id)</td>\n          <td>@pagelets(graph.id)</td>\n        </tr>\n      </table>\n    }\n\n    </body>\n</html>"
  },
  {
    "path": "sample-app-common/src/main/twirl/views/withoutBigPipe.scala.html",
    "content": "@(profile: data.Response, graph: data.Response, feed: data.Response, inbox: data.Response, ads: data.Response, search: data.Response)\n\n<html>\n  <head>\n    <link rel=\"stylesheet\" href=\"/assets/stylesheets/main.css\">\n  </head>\n  <body>\n    <h1>Without Big Pipe</h1>\n    @views.html.helpers.timing()\n    <table class=\"wrapper\">\n      <tr>\n        <td><div id=\"profile\">@views.html.helpers.module(profile)</div></td>\n        <td><div id=\"ads\">@views.html.helpers.module(ads)</div></td>\n        <td><div id=\"feed\">@views.html.helpers.module(feed)</div></td>\n      </tr>\n      <tr>\n        <td><div id=\"search\">@views.html.helpers.module(search)</div></td>\n        <td><div id=\"inbox\">@views.html.helpers.module(inbox)</div></td>\n        <td><div id=\"graph\">@views.html.helpers.module(graph)</div></td>\n      </tr>\n    </table>\n  </body>\n</html>"
  },
  {
    "path": "sample-app-common/src/test/scala/com/ybrikman/ping/BaseBigPipeSpec.scala",
    "content": "package com.ybrikman.ping\n\nimport play.api.test.WithBrowser\nimport data.FakeServiceClient\n\n/**\n * End-to-end tests of BigPipe functionality. The Scala and Java sample apps can extend this trait to run all the tests\n * in it.\n */\ntrait BaseBigPipeSpec extends PingSpecification {\n  \"The sample app\" should {\n    \"render the page without BigPipe\" in new WithBrowser(app = createTestComponents().app) {\n      browser.goTo(s\"http://localhost:$port/withoutBigPipe\")\n      browser.$(\"#profile .id\").getTexts.get(0) must equalTo(\"profile\")\n    }\n\n    \"render the page client-side with BigPipe\" in new WithBrowser(app = createTestComponents().app) {\n      browser.goTo(s\"http://localhost:$port/withBigPipe\")\n      browser.$(\"#profile .id\").getTexts.get(0) must equalTo(\"profile\")\n    }\n\n    \"render the page server-side with BigPipe\" in new WithBrowser(app = createTestComponents().app) {\n      browser.goTo(s\"http://localhost:$port/serverSideRendering\")\n      browser.$(\"#profile .id\").getTexts.get(0) must equalTo(\"profile\")\n    }\n\n    \"render the page client-side with BigPipe and Mustache.js JavaScript templates\" in new WithBrowser(app = createTestComponents().app) {\n      browser.goTo(s\"http://localhost:$port/clientSideTemplating\")\n      browser.$(\"#profile .id\").getTexts.get(0) must equalTo(\"profile\")\n    }\n\n    \"handle errors while rendering with BigPipe\" in new WithBrowser(app = createTestComponents().app) {\n      browser.goTo(s\"http://localhost:$port/errorHandling\")\n      browser.$(\"#profile .id\").getTexts.get(0) must equalTo(\"profile\")\n      browser.$(\"#feed .id\").getTexts.get(0) must equalTo(\"error\")\n    }\n\n    \"escape the body of pagelets when using BigPipe\" in new WithBrowser(app = createTestComponents().app) {\n      browser.goTo(s\"http://localhost:$port/escaping\")\n      browser.$(\"#shouldBeEscaped .id\").getTexts.get(0) must equalTo(FakeServiceClient.RESPONSE_TO_TEST_ESCAPING)\n    }\n  }\n}\n"
  },
  {
    "path": "sample-app-common/src/test/scala/com/ybrikman/ping/BaseBigPipeTimingSpec.scala",
    "content": "package com.ybrikman.ping\n\nimport com.ybrikman.ping.TimingHelper._\nimport com.ybrikman.ping.CustomRoutes._\nimport com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions\nimport com.ybrikman.ping.scalaapi.bigpipe.HtmlStreamImplicits._\nimport com.ybrikman.ping.scalaapi.bigpipe._\nimport data.FutureUtil\nimport play.api.libs.concurrent.Execution.Implicits._\nimport play.api.libs.iteratee.{Enumerator, Iteratee}\nimport play.api.libs.ws.WSClient\nimport play.api.mvc.{Action, Results}\nimport play.api.routing.Router\nimport play.api.routing.sird._\n\nimport scala.concurrent.{ExecutionContext, Future}\n\n/**\n * Tests that BigPipe is actually streaming data as soon as it's available and that chunks are not blocked anywhere.\n * The Scala and Java sample apps can extend this trait to run all the tests in it.\n */\ntrait BaseBigPipeTimingSpec extends PingSpecification {\n  \"BigPipe streaming\" should {\n    \"Send down the data in-order, only after all of it is available, without BigPipe\" in new WithWarmedUpPingTestServer(createTestComponents(withRouterToTestTimings)) {\n      val chunkTimings = getTimings(components.wsClient, s\"http://localhost:$port/withoutBigPipe\")\n      chunkTimings must not be empty\n\n      // Time-to-first-byte: make sure the first chunk was sent back after the maxDelay (within a tolerance of\n      // ToleranceInMillis)\n      val firstChunk = chunkTimings(0)\n      firstChunk.content mustEqual FirstChunkContent\n      firstChunk.timeElapsed must beGreaterThan(maxDelay - ToleranceInMillis) and beLessThan(maxDelay + ToleranceInMillis)\n\n      // Make sure the contents for each pagelet were sent back exactly once\n      val pageletContentTiming = PageletIndices.flatMap { index =>\n        chunkTimings.filter(_.content.contains(content(index)))\n      }\n      pageletContentTiming must have size PageletIndices.size\n\n      // Check that contents for each pagelet were delayed by the slowest pagelet and no more (within a tolerance of\n      // ToleranceInMillis)\n      val expectedTimingMatchers = PageletIndices.map { index =>\n        beGreaterThan(maxDelay - ToleranceInMillis) and beLessThan(maxDelay + ToleranceInMillis)\n      }\n      pageletContentTiming.map(_.timeElapsed) must contain(eachOf(expectedTimingMatchers:_*)).inOrder\n    }\n\n    \"Send down the data out-of-order, as soon as any of it is available, with client-side streaming\" in new WithWarmedUpPingTestServer(createTestComponents(withRouterToTestTimings)) {\n      val chunkTimings = getTimings(components.wsClient, s\"http://localhost:$port/withBigPipeClientSide\")\n      chunkTimings must not be empty\n\n      // Time-to-first-byte: make sure the first chunk was sent back almost immediately\n      val firstChunk = chunkTimings(0)\n      firstChunk.content mustEqual FirstChunkContent\n      firstChunk.timeElapsed must beLessThan(ToleranceInMillis)\n\n      // Placeholders: make sure all the placeholders were sent back almost immediately and exactly once\n      val pageletPlaceholderTiming = PageletIndices.flatMap { index =>\n        chunkTimings.filter(_.content.contains(placeholder(id(index))))\n      }\n      pageletPlaceholderTiming must have size PageletIndices.size\n      pageletPlaceholderTiming.map(_.timeElapsed) must contain(beLessThan(ToleranceInMillis)).forall\n\n      // Make sure the contents for each pagelet were sent back exactly once\n      val pageletContentTiming = PageletIndices.flatMap { index =>\n        chunkTimings.filter(_.content.contains(content(index)))\n      }\n      pageletContentTiming must have size PageletIndices.size\n\n      // Check that contents for each pagelet were delayed by no more and no less than\n      // DELAY_MULTIPLIER_IN_MILLIS (within a tolerance of ToleranceInMillis)\n      val expectedTimingMatchers = PageletIndices.map { index =>\n        val expecteDelay = delay(index)\n        beGreaterThan(expecteDelay - ToleranceInMillis) and beLessThan(expecteDelay + ToleranceInMillis)\n      }\n      pageletContentTiming.map(_.timeElapsed) must contain(eachOf(expectedTimingMatchers:_*)).inOrder\n    }\n\n    \"Send down the data in-order, as soon as it's available, with server-side streaming\" in new WithWarmedUpPingTestServer(createTestComponents(withRouterToTestTimings)) {\n      val chunkTimings = getTimings(components.wsClient, s\"http://localhost:$port/withBigPipeServerSide\")\n      chunkTimings must not be empty\n\n      // Time-to-first-byte: make sure the first chunk was sent back almost immediately\n      val firstChunk = chunkTimings(0)\n      firstChunk.content mustEqual FirstChunkContent\n      firstChunk.timeElapsed must beLessThan(ToleranceInMillis)\n\n      // Make sure the contents for each pagelet were sent back exactly once\n      val pageletContentTiming = PageletIndices.flatMap { index =>\n        chunkTimings.filter(_.content.contains(content(index)))\n      }\n      pageletContentTiming must have size PageletIndices.size\n\n      // Check that contents for each pagelet were delayed by the slowest pagelet and no more (within a tolerance of\n      // ToleranceInMillis)\n      val expectedTimingMatchers = PageletIndices.map { index =>\n        beGreaterThan(maxDelay - ToleranceInMillis) and beLessThan(maxDelay + ToleranceInMillis)\n      }\n      pageletContentTiming.map(_.timeElapsed) must contain(eachOf(expectedTimingMatchers:_*)).inOrder\n    }\n  }\n\n  private def getTimings(wsClient: WSClient, url: String): Seq[Timing] = {\n    val initialTimings = Timings()\n    val (_, bodyEnumerator) = await(wsClient.url(url).getStream())\n\n    val checkTiming = Iteratee.fold[Array[Byte], Timings](initialTimings) { (timings, chunk) =>\n      timings.addChunk(chunk)\n    }\n\n    val timings = await(bodyEnumerator.run(checkTiming))\n\n    // Useful for debugging\n    println(s\"Timings and content for url $url:\\n\")\n    timings.chunkTimings.foreach(timing => println(s\"----- ${timing.timeElapsed} ms -----\\n\\n${timing.content}\\n\\n\"))\n\n    timings.chunkTimings\n  }\n}\n\nobject TimingHelper {\n  val PageletIndices = 1 until 5\n\n  // Each pagelet id will have this prefix to make it easier to find in the stream of data\n  val IdPrefix = \"pagelet_id_\"\n\n  // The contents of each pagelet will have this prefix to make it easier to find in the stream of data\n  val ContentPrefix = \"pagelet_content_\"\n\n  // The placeholder for each pagelet will have this prefix to make it easier to find in the stream of data\n  val PlaceHolderPrefix = \"pagelet_placeholder_\"\n\n  // The contents of the very first chunk that should be sent back by each page\n  val FirstChunkContent = \"first_chunk_content\"\n\n  // Each pagelet will be delayed by this many milliseconds\n  val DelayMultiplierInMillis = 3000L\n\n  // With tests running in parallel, things may get a bit delayed, so check all timings within this tolerance\n  val ToleranceInMillis = DelayMultiplierInMillis / 10\n\n  def id(index: Int): String = {\n    s\"$IdPrefix$index\"\n  }\n\n  def content(index: Int): String = {\n    s\"$ContentPrefix$index\"\n  }\n\n  def delay(index: Int): Long = {\n    (PageletIndices.size - index) * DelayMultiplierInMillis\n  }\n\n  def maxDelay: Long = {\n    delay(PageletIndices.head)\n  }\n\n  def placeholder(id: String): String = {\n    s\"$PlaceHolderPrefix$id\"\n  }\n}\n\ncase class Timings(startTime: Long = System.currentTimeMillis(), chunkTimings: Seq[Timing] = Seq.empty) {\n  def addChunk(contents: Array[Byte]): Timings = {\n    val timeElapsed = System.currentTimeMillis() - startTime\n    copy(chunkTimings = chunkTimings :+ Timing(new String(contents, \"UTF-8\"), timeElapsed))\n  }\n}\n\ncase class Timing(content: String, timeElapsed: Long)\n\nclass MockTextPagelet(id: String, content: Future[String]) extends TextPagelet(id, content) {\n  override def renderPlaceholder(implicit ec: ExecutionContext): HtmlStream = {\n    HtmlStream.fromHtml(com.ybrikman.bigpipe.html.pageletServerSide(placeholder(id), PageletConstants.EmptyContent))\n  }\n}\n\nobject CustomRoutes {\n  def withRouterToTestTimings: Option[RouterComponents => Router] = {\n    def createRoutes(routerComponents: RouterComponents): Router = {\n      val futureUtil = new FutureUtil(routerComponents.actorSystem)\n\n      Router.from {\n        case GET(p\"/withoutBigPipe\") => Action.async {\n          val futures = mockRemoteServiceCalls(futureUtil).map(_._2)\n          Future.sequence(futures).map { contents =>\n            Results.Ok.chunked(Enumerator(FirstChunkContent).andThen(Enumerator(contents:_*)))\n          }\n        }\n        case GET(p\"/withBigPipeClientSide\") => Action {\n          val pagelets = mockRemoteServiceCalls(futureUtil).map { case (id, data) => new MockTextPagelet(id, data) }\n          Results.Ok.chunked(renderPagelets(PageletRenderOptions.ClientSide, pagelets))\n        }\n        case GET(p\"/withBigPipeServerSide\") => Action {\n          val pagelets = mockRemoteServiceCalls(futureUtil).map { case (id, data) => new MockTextPagelet(id, data) }\n          Results.Ok.chunked(renderPagelets(PageletRenderOptions.ServerSide, pagelets))\n        }\n        case GET(p\"/warmup\") => Action {\n          Results.Ok(\"warmup\")\n        }\n      }\n    }\n\n    Option(createRoutes _)\n  }\n\n  // Generate a series of Futures that represent remote calls. The Futures are returned in reverse order, from slowest\n  // to fastest, as a way to demonstrate the advantages of out-of-order client-side rendering.\n  private def mockRemoteServiceCalls(futureUtil: FutureUtil): Seq[(String, Future[String])] = {\n    PageletIndices.map { index =>\n      id(index) -> futureUtil.timeout(content(index), delay(index))\n    }\n  }\n\n  private def renderPagelets(renderOptions: PageletRenderOptions, pagelets: Seq[Pagelet]): HtmlStream = {\n    val bigPipe = new BigPipe(renderOptions, pagelets:_*)\n    bigPipe.render { renderedPagelets =>\n      pagelets.foldLeft(HtmlStream.fromString(FirstChunkContent)) { (stream, pagelet) =>\n        stream.andThen(renderedPagelets(pagelet.id))\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "sample-app-common/src/test/scala/com/ybrikman/ping/BaseDedupeSpec.scala",
    "content": "package com.ybrikman.ping\n\nimport play.api.test.WithBrowser\nimport scala.collection.JavaConverters._\n\n/**\n * An end-to-end test of the de-duping cache. The Scala and Java sample apps can extend this trait to run all the tests\n * in it.\n */\ntrait BaseDedupeSpec extends PingSpecification {\n  \"The Deduping controller\" should {\n    \"dedupe remote calls\" in new WithBrowser(app = createTestComponents().app) {\n      browser.goTo(s\"http://localhost:$port/dedupe\")\n      val values = browser.$(\".id\").getTexts.asScala\n\n      // First 3 values should be the same since they were de-duped, fourth should be different\n      values must have size 4\n      values(0) mustEqual values(1)\n      values(1) mustEqual values(2)\n      values(1) mustNotEqual values(3)\n    }\n  }\n}\n"
  },
  {
    "path": "sample-app-common/src/test/scala/com/ybrikman/ping/PingSpecification.scala",
    "content": "package com.ybrikman.ping\n\nimport akka.actor.ActorSystem\nimport org.specs2.execute.{AsResult, Result}\nimport play.api.Application\nimport play.api.libs.ws.WSClient\nimport play.api.routing.Router\nimport play.api.test.{PlaySpecification, DefaultAwaitTimeout, FutureAwaits, WithServer}\n\ntrait PingSpecification extends PlaySpecification with PingTestComponentsProvider\n\ntrait PingTestComponentsProvider {\n  def createTestComponents(customRoutes: Option[RouterComponents => Router] = None): PingTestComponents\n}\n\n/**\n * Common class that abstracts away whether the underlying app uses Java or Scala and how it's initialized.\n *\n * @param app\n * @param wsClient\n */\ncase class PingTestComponents(app: Application, wsClient: WSClient)\n\n/**\n * A bit of an ugly hack. Some test cases need custom routing. One in particular has a custom action that depends on\n * access to the ActorSystem. I can't figure out an easy way to solve this that works with both run-time dependency\n * injection (for Java apps) and compile-time dependency injection (for Scala apps), so this is an ugly workaround.\n *\n * @param actorSystem\n */\ncase class RouterComponents(actorSystem: ActorSystem)\n\n/**\n * An extension of specs2 \"Around\" that can be used to fire up a test server in one line.\n *\n * @param components\n */\nabstract class WithPingTestServer(val components: PingTestComponents) extends WithServer(app = components.app)\n\n/**\n * Same as WithPingTestServer, except this one hits the /warmup URL a bunch of times before running the test. This is\n * useful to ensure the app is fully up and running so that tests sensitive to timing are not thrown off by bootup\n * and initialization routines.\n *\n * @param components\n */\nabstract class WithWarmedUpPingTestServer(components: PingTestComponents) extends WithPingTestServer(components) with FutureAwaits with DefaultAwaitTimeout {\n  override def around[T](t: => T)(implicit evidence$3: AsResult[T]): Result = {\n    super.around {\n      warmup()\n      t\n    }\n  }\n\n  // Make sure the server is warmed up so our timing is not thrown off by bootup and initialization routines\n  private def warmup(): Unit = {\n    (0 until 15).foreach { _ =>\n      await(components.wsClient.url(s\"http://localhost:$port/warmup\").get())\n    }\n  }\n}"
  },
  {
    "path": "sample-app-java/app/controllers/Deduping.java",
    "content": "package controllers;\n\nimport com.fasterxml.jackson.databind.node.ArrayNode;\nimport com.ybrikman.ping.javaapi.promise.PromiseHelper;\nimport data.ServiceClient;\nimport data.UrlAndId;\nimport play.libs.F;\nimport play.libs.Json;\nimport play.libs.ws.WSResponse;\nimport play.mvc.Controller;\nimport play.mvc.Result;\n\nimport javax.inject.Inject;\n\n/**\n * This controller shows an example of how remote service calls can be transparently de-duped using the DedupingCache\n * to ensure that we only make one remote call for each unique URL.\n */\npublic class Deduping extends Controller {\n  private final ServiceClient serviceClient;\n\n  @Inject\n  public Deduping(ServiceClient serviceClient) {\n    this.serviceClient = serviceClient;\n  }\n  \n  public F.Promise<Result> index() {\n    // Call an endpoint on this same Play app that returns the request id, which should be unique for every incoming\n    // request\n    String baseUrl = \"http://\" + request().host();\n    String url1 = baseUrl + \"/mock/requestId\";\n    String url2 = baseUrl + \"/mock/requestId?foo=bar\";\n\n    // Thanks to the DedupingCache in the ServiceClient, all 3 calls to url1 will result in only a single remote call\n    // and the call to url2 will result in a separate call\n    F.Promise<WSResponse> promise1 = serviceClient.remoteCall(url1);\n    F.Promise<WSResponse> promise2 = serviceClient.remoteCall(url1);\n    F.Promise<WSResponse> promise3 = serviceClient.remoteCall(url1);\n    F.Promise<WSResponse> promise4 = serviceClient.remoteCall(url2);\n\n    return PromiseHelper.sequence(promise1, promise2, promise3, promise4).map((result1, result2, result3, result4) -> {\n      // We should expect to see the same request id for the first 3 requests (since deduping should ensure only one\n      // request is actually made) and a different id for the fourth one\n\n      UrlAndId urlAndId1 = new UrlAndId(url1, result1.getBody());\n      UrlAndId urlAndId2 = new UrlAndId(url1, result2.getBody());\n      UrlAndId urlAndId3 = new UrlAndId(url1, result3.getBody());\n      UrlAndId urlAndId4 = new UrlAndId(url2, result4.getBody());\n\n      return ok(views.html.dedupe.apply(urlAndId1, urlAndId2, urlAndId3, urlAndId4));\n    });\n  }\n}\n"
  },
  {
    "path": "sample-app-java/app/controllers/Mock.java",
    "content": "package controllers;\n\nimport play.mvc.Controller;\nimport play.mvc.Result;\n\n/**\n * This controller is used as a mock remote endpoint in other tests. This allows you to make actual remote calls, but\n * still completely control the response you get back.\n */\npublic class Mock extends Controller {\n\n  /**\n   * An endpoint that returns the request id of the incoming request\n   *\n   * @return\n   */\n  public Result requestId() {\n    return ok(ctx().id().toString());\n  }\n}\n"
  },
  {
    "path": "sample-app-java/app/controllers/MoreBigPipeExamples.java",
    "content": "package controllers;\n\nimport com.fasterxml.jackson.databind.JsonNode;\nimport com.ybrikman.ping.javaapi.bigpipe.BigPipe;\nimport com.ybrikman.ping.javaapi.bigpipe.HtmlPagelet;\nimport com.ybrikman.ping.javaapi.bigpipe.HtmlStreamHelper;\nimport com.ybrikman.ping.javaapi.bigpipe.JsonPagelet;\nimport com.ybrikman.ping.javaapi.bigpipe.Pagelet;\nimport com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions;\nimport data.Response;\nimport helper.FakeServiceClient;\nimport data.FakeServiceClient$;\nimport play.libs.F;\nimport play.mvc.Controller;\nimport play.mvc.Result;\nimport play.twirl.api.Html;\n\nimport javax.inject.Inject;\n\n/**\n * A few more BigPipe examples\n */\npublic class MoreBigPipeExamples extends Controller {\n  private final FakeServiceClient serviceClient;\n\n  @Inject\n  public MoreBigPipeExamples(FakeServiceClient serviceClient) {\n    this.serviceClient = serviceClient;\n  }\n\n  /**\n   * Renders the exact same page as WithBigPipe#index, but this time with server-side rendering. This will render all\n   * pagelets server-side and send them down in-order. The page load time will be longer than with out-of-order\n   * client-side rendering (albeit still faster than not using BigPipe at all), but the advantage is that server-side\n   * rendering does not depend on JavaScript, which is important for certain use cases (e.g. older browsers, search\n   * engine crawlers, SEO).\n   *\n   * @return\n   */\n  public Result serverSideRendering() {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    F.Promise<Response> profilePromise = serviceClient.fakeRemoteCallMedium(\"profile\");\n    F.Promise<Response> graphPromise = serviceClient.fakeRemoteCallMedium(\"graph\");\n    F.Promise<Response> feedPromise = serviceClient.fakeRemoteCallSlow(\"feed\");\n    F.Promise<Response> inboxPromise = serviceClient.fakeRemoteCallSlow(\"inbox\");\n    F.Promise<Response> adsPromise = serviceClient.fakeRemoteCallFast(\"ads\");\n    F.Promise<Response> searchPromise = serviceClient.fakeRemoteCallFast(\"search\");\n\n    // Convert each Promise into a Pagelet which will be rendered as HTML as soon as the data is available.\n    Pagelet profile = new HtmlPagelet(\"profile\", profilePromise.map(views.html.helpers.module::apply));\n    Pagelet graph = new HtmlPagelet(\"graph\", graphPromise.map(views.html.helpers.module::apply));\n    Pagelet feed = new HtmlPagelet(\"feed\", feedPromise.map(views.html.helpers.module::apply));\n    Pagelet inbox = new HtmlPagelet(\"inbox\", inboxPromise.map(views.html.helpers.module::apply));\n    Pagelet ads = new HtmlPagelet(\"ads\", adsPromise.map(views.html.helpers.module::apply));\n    Pagelet search = new HtmlPagelet(\"search\", searchPromise.map(views.html.helpers.module::apply));\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template. Note that we're using\n    // ServerSide rendering in this case.\n    BigPipe bigPipe = new BigPipe(PageletRenderOptions.ServerSide, profile, graph, feed, inbox, ads, search);\n    return ok(HtmlStreamHelper.toChunks(views.stream.withBigPipe.apply(bigPipe, profile, graph, feed, inbox, ads, search)));\n  }\n\n  /**\n   * Instead of rendering each pagelet server-side with Play's templating, you can send back JSON and render each\n   * pagelet with a client-side templating library such as mustache.js\n   *\n   * @return\n   */\n  public Result clientSideTemplating() {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    F.Promise<JsonNode> profilePromise = serviceClient.fakeRemoteCallJsonMedium(\"profile\");\n    F.Promise<JsonNode> graphPromise = serviceClient.fakeRemoteCallJsonMedium(\"graph\");\n    F.Promise<JsonNode> feedPromise = serviceClient.fakeRemoteCallJsonSlow(\"feed\");\n    F.Promise<JsonNode> inboxPromise = serviceClient.fakeRemoteCallJsonSlow(\"inbox\");\n    F.Promise<JsonNode> adsPromise = serviceClient.fakeRemoteCallJsonFast(\"ads\");\n    F.Promise<JsonNode> searchPromise = serviceClient.fakeRemoteCallJsonFast(\"search\");\n\n    Pagelet profile = new JsonPagelet(\"profile\", profilePromise);\n    Pagelet graph = new JsonPagelet(\"graph\", graphPromise);\n    Pagelet feed = new JsonPagelet(\"feed\", feedPromise);\n    Pagelet inbox = new JsonPagelet(\"inbox\", inboxPromise);\n    Pagelet ads = new JsonPagelet(\"ads\", adsPromise);\n    Pagelet search = new JsonPagelet(\"search\", searchPromise);\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    BigPipe bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search);\n    return ok(HtmlStreamHelper.toChunks(views.stream.clientSideTemplating.apply(bigPipe, profile, graph, feed, inbox, ads, search)));\n  }\n\n  /**\n   * Shows an example of how to handle an error that occurs part way through streaming a response to the browser. Since\n   * you've already sent back the headers with a 200 OK, it's too late to send back a 500 error page, so instead, you\n   * have to inject JavaScript into the stream that will show an appropriate error page.\n   *\n   * @return\n   */\n  public Result errorHandling() {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    F.Promise<Response> profilePromise = serviceClient.fakeRemoteCallMedium(\"profile\");\n    F.Promise<Response> graphPromise = serviceClient.fakeRemoteCallMedium(\"graph\");\n    F.Promise<Response> feedPromise = serviceClient.fakeRemoteCallErrorSlow(\"feed\");\n    F.Promise<Response> inboxPromise = serviceClient.fakeRemoteCallSlow(\"inbox\");\n    F.Promise<Response> adsPromise = serviceClient.fakeRemoteCallFast(\"ads\");\n    F.Promise<Response> searchPromise = serviceClient.fakeRemoteCallFast(\"search\");\n\n    // Convert each Promise into a Pagelet which will be rendered as HTML as soon as the data is available. Note that\n    // the render method used here will also handle the case where the Future completes with an error by rendering an\n    // error message.\n    Pagelet profile = new HtmlPagelet(\"profile\", render(profilePromise));\n    Pagelet graph = new HtmlPagelet(\"graph\", render(graphPromise));\n    Pagelet feed = new HtmlPagelet(\"feed\", render(feedPromise));\n    Pagelet inbox = new HtmlPagelet(\"inbox\", render(inboxPromise));\n    Pagelet ads = new HtmlPagelet(\"ads\", render(adsPromise));\n    Pagelet search = new HtmlPagelet(\"search\", render(searchPromise));\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    BigPipe bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search);\n    return ok(HtmlStreamHelper.toChunks(views.stream.withBigPipe.apply(bigPipe, profile, graph, feed, inbox, ads, search)));\n  }\n\n  /**\n   * Shows an example of how BigPipe escapes the contents of your pagelets so\n   * they cannot break out of their containing HTML elements (which are\n   * intentionally invisible).\n   *\n   * @return\n   */\n  public Result escaping() {\n    F.Promise<JsonNode> shouldBeEscapedPromise = serviceClient.fakeRemoteCallJsonFast(FakeServiceClient$.MODULE$.RESPONSE_TO_TEST_ESCAPING());\n\n    Pagelet shouldBeEscaped = new JsonPagelet(\"shouldBeEscaped\", shouldBeEscapedPromise);\n\n    BigPipe bigPipe = new BigPipe(PageletRenderOptions.ClientSide, shouldBeEscaped);\n\n    return ok(HtmlStreamHelper.toChunks(views.stream.escaping.apply(bigPipe, shouldBeEscaped)));\n  }\n\n  /**\n   * When the given Future redeems, render it with the module template. If the Future fails, render it with the\n   * error template.\n   *\n   * @param dataPromise\n   * @return\n   */\n  private F.Promise<Html> render(F.Promise<Response> dataPromise) {\n    return dataPromise.map(views.html.helpers.module::apply).recover(views.html.helpers.error::apply);\n  }\n}\n"
  },
  {
    "path": "sample-app-java/app/controllers/WithBigPipe.java",
    "content": "package controllers;\n\nimport com.ybrikman.ping.javaapi.bigpipe.BigPipe;\nimport com.ybrikman.ping.javaapi.bigpipe.HtmlPagelet;\nimport com.ybrikman.ping.javaapi.bigpipe.HtmlStreamHelper;\nimport com.ybrikman.ping.javaapi.bigpipe.Pagelet;\nimport com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions;\nimport data.Response;\nimport helper.FakeServiceClient;\nimport play.libs.F;\nimport play.mvc.Controller;\nimport play.mvc.Result;\n\nimport javax.inject.Inject;\n\npublic class WithBigPipe extends Controller {\n  private final FakeServiceClient serviceClient;\n\n  @Inject\n  public WithBigPipe(FakeServiceClient serviceClient) {\n    this.serviceClient = serviceClient;\n  }\n  \n  public Result index() {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    F.Promise<Response> profilePromise = serviceClient.fakeRemoteCallMedium(\"profile\");\n    F.Promise<Response> graphPromise = serviceClient.fakeRemoteCallMedium(\"graph\");\n    F.Promise<Response> feedPromise = serviceClient.fakeRemoteCallSlow(\"feed\");\n    F.Promise<Response> inboxPromise = serviceClient.fakeRemoteCallSlow(\"inbox\");\n    F.Promise<Response> adsPromise = serviceClient.fakeRemoteCallFast(\"ads\");\n    F.Promise<Response> searchPromise = serviceClient.fakeRemoteCallFast(\"search\");\n\n    // Convert each Promise into a Pagelet which will be rendered as HTML as soon as the data is available.\n    Pagelet profile = new HtmlPagelet(\"profile\", profilePromise.map(views.html.helpers.module::apply));\n    Pagelet graph = new HtmlPagelet(\"graph\", graphPromise.map(views.html.helpers.module::apply));\n    Pagelet feed = new HtmlPagelet(\"feed\", feedPromise.map(views.html.helpers.module::apply));\n    Pagelet inbox = new HtmlPagelet(\"inbox\", inboxPromise.map(views.html.helpers.module::apply));\n    Pagelet ads = new HtmlPagelet(\"ads\", adsPromise.map(views.html.helpers.module::apply));\n    Pagelet search = new HtmlPagelet(\"search\", searchPromise.map(views.html.helpers.module::apply));\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    BigPipe bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search);\n    return ok(HtmlStreamHelper.toChunks(views.stream.withBigPipe.apply(bigPipe, profile, graph, feed, inbox, ads, search)));\n  }\n}\n"
  },
  {
    "path": "sample-app-java/app/controllers/WithoutBigPipe.java",
    "content": "package controllers;\n\nimport com.ybrikman.ping.javaapi.promise.PromiseHelper;\nimport data.Response;\nimport helper.FakeServiceClient;\nimport play.libs.F;\nimport play.mvc.Controller;\nimport play.mvc.Result;\n\nimport javax.inject.Inject;\n\npublic class WithoutBigPipe extends Controller {\n\n  private final FakeServiceClient serviceClient;\n\n  @Inject\n  public WithoutBigPipe(FakeServiceClient serviceClient) {\n    this.serviceClient = serviceClient;\n  }\n\n  public F.Promise<Result> index() {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    F.Promise<Response> profilePromise = serviceClient.fakeRemoteCallMedium(\"profile\");\n    F.Promise<Response> graphPromise = serviceClient.fakeRemoteCallMedium(\"graph\");\n    F.Promise<Response> feedPromise = serviceClient.fakeRemoteCallSlow(\"feed\");\n    F.Promise<Response> inboxPromise = serviceClient.fakeRemoteCallSlow(\"inbox\");\n    F.Promise<Response> adsPromise = serviceClient.fakeRemoteCallFast(\"ads\");\n    F.Promise<Response> searchPromise = serviceClient.fakeRemoteCallFast(\"search\");\n\n    return PromiseHelper\n        .sequence(profilePromise, graphPromise, feedPromise, inboxPromise, adsPromise, searchPromise)\n        .map((profile, graph, feed, inbox, ads, search) ->\n          ok(views.html.withoutBigPipe.apply(profile, graph, feed, inbox, ads, search))\n        );\n  }\n}\n"
  },
  {
    "path": "sample-app-java/app/data/ServiceClient.java",
    "content": "package data;\n\nimport com.ybrikman.ping.javaapi.dedupe.DedupingCache;\nimport play.libs.F;\nimport play.libs.ws.WSClient;\nimport play.libs.ws.WSResponse;\n\nimport javax.inject.Inject;\n\npublic class ServiceClient {\n  private final WSClient ws;\n  private final DedupingCache<String, F.Promise<WSResponse>> cache;\n\n  @Inject\n  public ServiceClient(WSClient ws, DedupingCache<String, F.Promise<WSResponse>> cache) {\n    this.ws = ws;\n    this.cache = cache;\n  }\n\n  public F.Promise<WSResponse> remoteCall(String url) {\n    return cache.get(url, () -> ws.url(url).get());\n  }\n}\n"
  },
  {
    "path": "sample-app-java/app/helper/FakeServiceClient.java",
    "content": "package helper;\n\nimport akka.actor.ActorSystem;\nimport com.fasterxml.jackson.databind.JsonNode;\nimport data.FutureUtil;\nimport data.Response;\nimport play.api.libs.json.JsValue;\nimport play.libs.F;\nimport play.libs.Json;\n\nimport javax.inject.Inject;\n\npublic class FakeServiceClient {\n  private final data.FakeServiceClient delegate;\n\n  @Inject\n  public FakeServiceClient(ActorSystem actorSystem) {\n    delegate = new data.FakeServiceClient(new FutureUtil(actorSystem));\n  }\n\n  public F.Promise<Response> fakeRemoteCallFast(String id) {\n    return F.Promise.wrap(delegate.fakeRemoteCallFast(id));\n  }\n\n  public F.Promise<Response> fakeRemoteCallMedium(String id) {\n    return F.Promise.wrap(delegate.fakeRemoteCallMedium(id));\n  }\n\n  public F.Promise<Response> fakeRemoteCallSlow(String id) {\n    return F.Promise.wrap(delegate.fakeRemoteCallSlow(id));\n  }\n\n  public F.Promise<Response> fakeRemoteCall(String id, long delayInMillis) {\n    return F.Promise.wrap(delegate.fakeRemoteCall(id, delayInMillis));\n  }\n\n  public F.Promise<JsonNode> fakeRemoteCallJsonFast(String id) {\n    return toJsonNode(F.Promise.wrap(delegate.fakeRemoteCallJsonFast(id)));\n  }\n\n  public F.Promise<JsonNode> fakeRemoteCallJsonMedium(String id) {\n    return toJsonNode(F.Promise.wrap(delegate.fakeRemoteCallJsonMedium(id)));\n  }\n\n  public F.Promise<JsonNode> fakeRemoteCallJsonSlow(String id) {\n    return toJsonNode(F.Promise.wrap(delegate.fakeRemoteCallJsonSlow(id)));\n  }\n\n  public F.Promise<JsonNode> fakeRemoteCallJson(String id, long delayInMillis) {\n    return toJsonNode(F.Promise.wrap(delegate.fakeRemoteCallJson(id, delayInMillis)));\n  }\n\n  private F.Promise<JsonNode> toJsonNode(F.Promise<JsValue> jsValuePromise) {\n    return jsValuePromise.map(jsValue -> Json.parse(jsValue.toString()));\n  }\n\n  public F.Promise<Response> fakeRemoteCallErrorFast(String id) {\n    return F.Promise.wrap(delegate.fakeRemoteCallErrorFast(id));\n  }\n\n  public F.Promise<Response> fakeRemoteCallErrorMedium(String id) {\n    return F.Promise.wrap(delegate.fakeRemoteCallErrorMedium(id));\n  }\n\n  public F.Promise<Response> fakeRemoteCallErrorSlow(String id) {\n    return F.Promise.wrap(delegate.fakeRemoteCallErrorSlow(id));\n  }\n\n  public F.Promise<Response> fakeRemoteCallError(String id, long delayInMillis) {\n    return F.Promise.wrap(delegate.fakeRemoteCallError(id, delayInMillis));\n  }\n}\n"
  },
  {
    "path": "sample-app-java/app/loader/Filters.java",
    "content": "package loader;\n\nimport com.ybrikman.ping.javaapi.dedupe.CacheFilter;\nimport com.ybrikman.ping.javaapi.dedupe.DedupingCache;\nimport play.api.mvc.EssentialFilter;\nimport play.http.HttpFilters;\nimport play.libs.F;\nimport play.libs.ws.WSResponse;\n\nimport javax.inject.Inject;\n\n/**\n * Custom Filters for this app. Play knows to load this class because we put it into conf/application.conf\n */\npublic class Filters implements HttpFilters {\n\n  private final CacheFilter<String, F.Promise<WSResponse>> cacheFilter;\n\n  @Inject\n  public Filters(DedupingCache<String, F.Promise<WSResponse>> cache) {\n    cacheFilter = new CacheFilter<>(cache);\n  }\n\n  @Override\n  public EssentialFilter[] filters() {\n    return new EssentialFilter[]{cacheFilter};\n  }\n}\n"
  },
  {
    "path": "sample-app-java/conf/application.conf",
    "content": "play.http.filters=loader.Filters\nplay.crypto.secret=\"AkZXuiBfDLbr4;ZqVdfLZpHFZ:q4hryMV=0AGF25aYOE9`bWlRWbv/C=U=<>Kbxv\"\nplay.i18n.langs=[\"en\"]"
  },
  {
    "path": "sample-app-java/conf/routes",
    "content": "# Routes\n# This file defines all application routes (Higher priority routes first)\n# ~~~~\n\n# BigPipe examples\nGET        /withoutBigPipe           controllers.WithoutBigPipe.index\nGET        /withBigPipe              controllers.WithBigPipe.index\nGET        /serverSideRendering      controllers.MoreBigPipeExamples.serverSideRendering\nGET        /clientSideTemplating     controllers.MoreBigPipeExamples.clientSideTemplating\nGET        /errorHandling            controllers.MoreBigPipeExamples.errorHandling\nGET        /escaping                 controllers.MoreBigPipeExamples.escaping\n\n# Deduping examples\nGET        /dedupe                   controllers.Deduping.index\nGET        /mock/requestId           controllers.Mock.requestId\n\n# Map static resources from the /public folder to the /assets URL path\nGET        /assets/*file             controllers.Assets.versioned(path=\"/public\", file: Asset)\n\n\n\n\n"
  },
  {
    "path": "sample-app-java/test/com/ybrikman/ping/PingJavaTestComponents.scala",
    "content": "package com.ybrikman.ping\n\nimport akka.actor.ActorSystem\nimport play.api.libs.ws.WSClient\nimport play.api.mvc.{RequestHeader, Handler}\nimport play.api.routing.Router\nimport play.api.test.{FakeRequest, FakeApplication}\n\ntrait PingJavaTestComponents extends PingTestComponentsProvider {\n  override def createTestComponents(customRoutes: Option[(RouterComponents) => Router]): PingTestComponents = {\n    val initialApp = FakeApplication()\n    val actorSystem = initialApp.injector.instanceOf(classOf[ActorSystem])\n    val wsClient = initialApp.injector.instanceOf(classOf[WSClient])\n\n    val routes = customRoutes\n      .map(f => f(RouterComponents(actorSystem)))\n      .map(routerToPartialFunction)\n      .getOrElse(PartialFunction.empty)\n\n    val app = initialApp.copy(withRoutes = routes)\n\n    PingTestComponents(app, wsClient)\n  }\n\n  private def routerToPartialFunction(router: Router): PartialFunction[(String, String), Handler] = {\n    val toRequestHeader: PartialFunction[(String, String), RequestHeader] = {\n      case (method, path) => FakeRequest(method, path)\n    }\n\n    toRequestHeader.andThen(router.routes)\n  }\n}\n"
  },
  {
    "path": "sample-app-java/test/com/ybrikman/ping/Tests.scala",
    "content": "package com.ybrikman.ping\n\nclass DedupeSpec extends BaseDedupeSpec with PingJavaTestComponents\n\nclass BigPipeSpec extends BaseBigPipeSpec with PingJavaTestComponents\n\nclass BigPipeTimingSpec extends BaseBigPipeTimingSpec with PingJavaTestComponents\n"
  },
  {
    "path": "sample-app-scala/app/controllers/Deduping.scala",
    "content": "package controllers\n\nimport data.{UrlAndId, ServiceClient}\nimport play.api.mvc.{Action, Controller}\nimport play.api.libs.concurrent.Execution.Implicits._\n\n/**\n * This controller shows an example of how remote service calls can be transparently de-duped using the DedupingCache\n * to ensure that we only make one remote call for each unique URL.\n *\n * @param serviceClient\n */\nclass Deduping(serviceClient: ServiceClient) extends Controller {\n\n  def index = Action.async { implicit request =>\n    // Call an endpoint on this same Play app that returns the request id, which should be unique for every incoming\n    // request\n    val url1 = s\"http://${request.host}/mock/requestId\"\n    val url2 = s\"http://${request.host}/mock/requestId?foo=bar\"\n\n    // Thanks to the DedupingCache in the ServiceClient, all 3 calls to url1 will result in only a single remote call\n    // and the call to url2 will result in a separate call\n    val future1 = serviceClient.remoteCall(url1)\n    val future2 = serviceClient.remoteCall(url1)\n    val future3 = serviceClient.remoteCall(url1)\n    val future4 = serviceClient.remoteCall(url2)\n\n    for {\n      result1 <- future1\n      result2 <- future2\n      result3 <- future3\n      result4 <- future4\n    } yield {\n      // We should expect to see the same request id for the first 3 requests (since deduping should ensure only one\n      // request is actually made) and a different id for the fourth one\n      Ok(views.html.dedupe(UrlAndId(url1, result1.body), UrlAndId(url1, result2.body), UrlAndId(url1, result3.body), UrlAndId(url2, result4.body)))\n    }\n  }\n}\n"
  },
  {
    "path": "sample-app-scala/app/controllers/Mock.scala",
    "content": "package controllers\n\nimport play.api.mvc.{Controller, Action}\n\n/**\n * This controller is used as a mock remote endpoint in other tests. This allows you to make actual remote calls, but\n * still completely control the response you get back.\n */\nclass Mock extends Controller {\n\n  /**\n   * An endpoint that returns the request id of the incoming request\n   *\n   * @return\n   */\n  def requestId = Action { request =>\n    Ok(request.id.toString)\n  }\n}\n"
  },
  {
    "path": "sample-app-scala/app/controllers/MoreBigPipeExamples.scala",
    "content": "package controllers\n\nimport com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions\nimport com.ybrikman.ping.scalaapi.bigpipe.HtmlStreamImplicits._\nimport com.ybrikman.ping.scalaapi.bigpipe._\nimport data.{FakeServiceClient, Response}\nimport play.api.libs.concurrent.Execution.Implicits._\nimport play.api.mvc.{Action, Controller}\nimport play.twirl.api.Html\n\nimport scala.concurrent.Future\n\n/**\n * A few more BigPipe examples\n *\n * @param serviceClient\n */\nclass MoreBigPipeExamples(serviceClient: FakeServiceClient) extends Controller {\n\n  /**\n   * Renders the exact same page as WithBigPipe#index, but this time with server-side rendering. This will render all\n   * pagelets server-side and send them down in-order. The page load time will be longer than with out-of-order\n   * client-side rendering (albeit still faster than not using BigPipe at all), but the advantage is that server-side\n   * rendering does not depend on JavaScript, which is important for certain use cases (e.g. older browsers, search\n   * engine crawlers, SEO).\n   *\n   * @return\n   */\n  def serverSideRendering = Action {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    val profileFuture = serviceClient.fakeRemoteCallMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallFast(\"search\")\n\n    // Convert each Future into a Pagelet which will be rendered as HTML as soon as the data is available\n    val profile = HtmlPagelet(\"profile\", profileFuture.map(views.html.helpers.module.apply))\n    val graph = HtmlPagelet(\"graph\", graphFuture.map(views.html.helpers.module.apply))\n    val feed = HtmlPagelet(\"feed\", feedFuture.map(views.html.helpers.module.apply))\n    val inbox = HtmlPagelet(\"inbox\", inboxFuture.map(views.html.helpers.module.apply))\n    val ads = HtmlPagelet(\"ads\", adsFuture.map(views.html.helpers.module.apply))\n    val search = HtmlPagelet(\"search\", searchFuture.map(views.html.helpers.module.apply))\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template. Note that we're using\n    // ServerSide rendering in this case.\n    val bigPipe = new BigPipe(PageletRenderOptions.ServerSide, profile, graph, feed, inbox, ads, search)\n    Ok.chunked(views.stream.withBigPipe(bigPipe, profile, graph, feed, inbox, ads, search))\n  }\n\n  /**\n   * Instead of rendering each pagelet server-side with Play's templating, you can send back JSON and render each\n   * pagelet with a client-side templating library such as mustache.js\n   *\n   * @return\n   */\n  def clientSideTemplating = Action {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    val profileFuture = serviceClient.fakeRemoteCallJsonMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallJsonMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallJsonSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallJsonSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallJsonFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallJsonFast(\"search\")\n\n    // Convert each Future into a Pagelet which will send the JSON to the browser as soon as it's available\n    val profile = JsonPagelet(\"profile\", profileFuture)\n    val graph = JsonPagelet(\"graph\", graphFuture)\n    val feed = JsonPagelet(\"feed\", feedFuture)\n    val inbox = JsonPagelet(\"inbox\", inboxFuture)\n    val ads = JsonPagelet(\"ads\", adsFuture)\n    val search = JsonPagelet(\"search\", searchFuture)\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    val bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search)\n    Ok.chunked(views.stream.clientSideTemplating(bigPipe, profile, graph, feed, inbox, ads, search))\n  }\n\n  /**\n   * Shows an example of how to handle an error that occurs part way through streaming a response to the browser. Since\n   * you've already sent back the headers with a 200 OK, it's too late to send back a 500 error page, so instead, you\n   * have to inject JavaScript into the stream that will show an appropriate error page.\n   *\n   * @return\n   */\n  def errorHandling = Action {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow. One of the calls (the feed) will fail with an error.\n    val profileFuture = serviceClient.fakeRemoteCallMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallErrorSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallFast(\"search\")\n\n    // Convert each Future into a Pagelet which will be rendered as HTML as soon as the data is available. Note that\n    // the render method used here will also handle the case where the Future completes with an error by rendering an\n    // error message.\n    val profile = HtmlPagelet(\"profile\", render(profileFuture))\n    val graph = HtmlPagelet(\"graph\", render(graphFuture))\n    val feed = HtmlPagelet(\"feed\", render(feedFuture))\n    val inbox = HtmlPagelet(\"inbox\", render(inboxFuture))\n    val ads = HtmlPagelet(\"ads\", render(adsFuture))\n    val search = HtmlPagelet(\"search\", render(searchFuture))\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    val bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search)\n    Ok.chunked(views.stream.withBigPipe(bigPipe, profile, graph, feed, inbox, ads, search))\n  }\n\n  /**\n   * Shows an example of how BigPipe escapes the contents of your pagelets so\n   * they cannot break out of their containing HTML elements (which are\n   * intentionally invisible).\n   *\n   * @return\n   */\n  def escaping = Action {\n    val shouldBeEscapedFuture = serviceClient.fakeRemoteCallJsonFast(FakeServiceClient.RESPONSE_TO_TEST_ESCAPING)\n\n    val shouldBeEscaped = JsonPagelet(\"shouldBeEscaped\", shouldBeEscapedFuture)\n\n    val bigPipe = new BigPipe(PageletRenderOptions.ClientSide, shouldBeEscaped)\n    Ok.chunked(views.stream.escaping(bigPipe, shouldBeEscaped))\n  }\n\n  /**\n   * When the given Future redeems, render it with the module template. If the Future fails, render it with the\n   * error template.\n   *\n   * @param dataFuture\n   * @return\n   */\n  private def render(dataFuture: Future[Response]): Future[Html] = {\n    dataFuture.map(views.html.helpers.module.apply).recover { case t: Throwable => views.html.helpers.error(t) }\n  }\n}\n"
  },
  {
    "path": "sample-app-scala/app/controllers/WithBigPipe.scala",
    "content": "package controllers\n\nimport com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions\nimport com.ybrikman.ping.scalaapi.bigpipe.HtmlStreamImplicits._\nimport com.ybrikman.ping.scalaapi.bigpipe.{BigPipe, HtmlPagelet}\nimport data.FakeServiceClient\nimport play.api.libs.concurrent.Execution.Implicits._\nimport play.api.mvc.{Action, Controller}\n\n/**\n * A page that uses BigPipe style streaming to show you how much faster it is to load.\n * \n * @param serviceClient\n */\nclass WithBigPipe(serviceClient: FakeServiceClient) extends Controller {\n\n  def index = Action {\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    val profileFuture = serviceClient.fakeRemoteCallMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallFast(\"search\")\n\n    // Convert each Future into a Pagelet which will be rendered as HTML as soon as the data is available\n    val profile = HtmlPagelet(\"profile\", profileFuture.map(views.html.helpers.module.apply))\n    val graph = HtmlPagelet(\"graph\", graphFuture.map(views.html.helpers.module.apply))\n    val feed = HtmlPagelet(\"feed\", feedFuture.map(views.html.helpers.module.apply))\n    val inbox = HtmlPagelet(\"inbox\", inboxFuture.map(views.html.helpers.module.apply))\n    val ads = HtmlPagelet(\"ads\", adsFuture.map(views.html.helpers.module.apply))\n    val search = HtmlPagelet(\"search\", searchFuture.map(views.html.helpers.module.apply))\n\n    // Use BigPipe to compose the pagelets and render them immediately using a streaming template\n    val bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search)\n    Ok.chunked(views.stream.withBigPipe(bigPipe, profile, graph, feed, inbox, ads, search))\n  }\n}\n"
  },
  {
    "path": "sample-app-scala/app/controllers/WithoutBigPipe.scala",
    "content": "package controllers\n\nimport data.FakeServiceClient\nimport play.api.mvc.{Action, Controller}\nimport play.api.libs.concurrent.Execution.Implicits._\n\n/**\n * An example that shows a page that does NOT use BigPipe streaming and how much slower it is to load.\n *\n * @param serviceClient\n */\nclass WithoutBigPipe(serviceClient: FakeServiceClient) extends Controller {\n\n  def index = Action.async { implicit request =>\n    // Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls\n    // will be fast, some medium, and some slow.\n    val profileFuture = serviceClient.fakeRemoteCallMedium(\"profile\")\n    val graphFuture = serviceClient.fakeRemoteCallMedium(\"graph\")\n    val feedFuture = serviceClient.fakeRemoteCallSlow(\"feed\")\n    val inboxFuture = serviceClient.fakeRemoteCallSlow(\"inbox\")\n    val adsFuture = serviceClient.fakeRemoteCallFast(\"ads\")\n    val searchFuture = serviceClient.fakeRemoteCallFast(\"search\")\n\n    // Wait for all the remote calls to complete\n    for {\n      profile <- profileFuture\n      graph <- graphFuture\n      feed <- feedFuture\n      inbox <- inboxFuture\n      ads <- adsFuture\n      search <- searchFuture\n    } yield {\n      // Render the template once all the data is available\n      Ok(views.html.withoutBigPipe(profile, graph, feed, inbox, ads, search))\n    }\n  }\n}\n"
  },
  {
    "path": "sample-app-scala/app/data/ServiceClient.scala",
    "content": "package data\n\nimport com.ybrikman.ping.scalaapi.dedupe.DedupingCache\nimport play.api.libs.ws.{WSResponse, WSClient}\nimport play.api.libs.concurrent.Execution.Implicits._\nimport play.api.mvc.RequestHeader\n\nimport scala.concurrent.Future\n\nclass ServiceClient(ws: WSClient, cache: DedupingCache[String, Future[WSResponse]]) {\n\n  def remoteCall(url: String)(implicit rh: RequestHeader): Future[WSResponse] = {\n    cache.get(url, ws.url(url).get())\n  }\n}\n"
  },
  {
    "path": "sample-app-scala/app/loader/PingApplicationLoader.scala",
    "content": "package loader\n\nimport com.ybrikman.ping.scalaapi.dedupe.{CacheFilter, DedupingCache}\nimport controllers._\nimport data.{ServiceClient, FutureUtil, FakeServiceClient}\nimport play.api.libs.ws.WSResponse\nimport play.api.libs.ws.ning.NingWSComponents\nimport play.api.routing.Router\nimport play.api.{BuiltInComponentsFromContext, Application, ApplicationLoader}\nimport play.api.ApplicationLoader.Context\nimport router.Routes\nimport play.api.libs.concurrent.Execution.Implicits._\n\nimport scala.concurrent.Future\n\n/**\n * This is the entry point for loading this Play app, as configured in conf/application.conf.\n */\nclass PingApplicationLoader extends ApplicationLoader {\n  override def load(context: Context): Application = {\n    new PingComponents(context).application\n  }\n}\n\n/**\n * Compile-time dependency injection for this Play app.\n *\n * @param context\n */\nclass PingComponents(context: Context) extends BuiltInComponentsFromContext(context) with NingWSComponents {\n  val cache = new DedupingCache[String, Future[WSResponse]]\n  val futureUtil = new FutureUtil(actorSystem)\n\n  val fakeServiceClient = new FakeServiceClient(futureUtil)\n  val serviceClient = new ServiceClient(wsClient, cache)\n\n  val withoutBigPipe = new WithoutBigPipe(fakeServiceClient)\n  val withBigPipe = new WithBigPipe(fakeServiceClient)\n  val moreBigPipeExamples = new MoreBigPipeExamples(fakeServiceClient)\n\n  val deduping = new Deduping(serviceClient)\n  val mock = new Mock\n\n  val assets = new Assets(httpErrorHandler)\n\n  val routes =  new Routes(\n    httpErrorHandler,\n    withoutBigPipe,\n    withBigPipe,\n    moreBigPipeExamples,\n    deduping,\n    mock,\n    assets)\n\n  override val router: Router = routes\n\n  val cacheFilter = new CacheFilter(cache)\n  override lazy val httpFilters = Seq(cacheFilter)\n}\n"
  },
  {
    "path": "sample-app-scala/conf/application.conf",
    "content": "play.application.loader=loader.PingApplicationLoader\nplay.crypto.secret=\"AkZXuiBfDLbr4;ZqVdfLZpHFZ:q4hryMV=0AGF25aYOE9`bWlRWbv/C=U=<>Kbxv\"\nplay.i18n.langs=[\"en\"]"
  },
  {
    "path": "sample-app-scala/conf/routes",
    "content": "# Routes\n# This file defines all application routes (Higher priority routes first)\n# ~~~~\n\n# Examples without BigPipe\nGET        /withoutBigPipe        controllers.WithoutBigPipe.index\n\n# Examples with BigPipe\nGET        /withBigPipe           controllers.WithBigPipe.index\nGET        /serverSideRendering   controllers.MoreBigPipeExamples.serverSideRendering\nGET        /clientSideTemplating  controllers.MoreBigPipeExamples.clientSideTemplating\nGET        /errorHandling         controllers.MoreBigPipeExamples.errorHandling\nGET        /escaping              controllers.MoreBigPipeExamples.escaping\n\n# Deduping examples\nGET        /dedupe                controllers.Deduping.index\nGET        /mock/requestId        controllers.Mock.requestId\n\n# Map static resources from the /public folder to the /assets URL path\nGET        /assets/*file          controllers.Assets.versioned(path=\"/public\", file: Asset)\n\n\n\n\n"
  },
  {
    "path": "sample-app-scala/test/com/ybrikman/ping/PingScalaTestComponents.scala",
    "content": "package com.ybrikman.ping\n\nimport loader.PingComponents\nimport play.api.routing.Router\nimport play.api.{Mode, Environment, ApplicationLoader}\n\ntrait PingScalaTestComponents extends PingTestComponentsProvider {\n\n  override def createTestComponents(customRoutes: Option[(RouterComponents) => Router]): PingTestComponents = {\n    val components = new PingComponentsForTest(createContext, customRoutes)\n    PingTestComponents(components.application, components.wsClient)\n  }\n\n  private def createContext: ApplicationLoader.Context = {\n    val env = new Environment(new java.io.File(\".\"), ApplicationLoader.getClass.getClassLoader, Mode.Test)\n    ApplicationLoader.createContext(env)\n  }\n}\n\nclass PingComponentsForTest(context: ApplicationLoader.Context, customRoutes: Option[(RouterComponents) => Router]) extends PingComponents(context) {\n  override val router: Router = customRoutes.map(f => f(RouterComponents(actorSystem))).getOrElse(routes)\n}\n"
  },
  {
    "path": "sample-app-scala/test/com/ybrikman/ping/Tests.scala",
    "content": "package com.ybrikman.ping\n\nclass DedupeSpec extends BaseDedupeSpec with PingScalaTestComponents\n\nclass BigPipeSpec extends BaseBigPipeSpec with PingScalaTestComponents\n\nclass BigPipeTimingSpec extends BaseBigPipeTimingSpec with PingScalaTestComponents"
  },
  {
    "path": "version.sbt",
    "content": "version in ThisBuild := \"0.0.14-SNAPSHOT\""
  }
]