Embeding Play using AkkaHttpServer and making conf/routes to be also available


(Francis Brosnan Blázquez) #1

Hello.
We attempting to integrate Play with AkkaHttpServer into our MQTT Akka implementation [1] as indicated here:

https://www.playframework.com/documentation/2.6.x/ScalaEmbeddingPlayAkkaHttp

In particular, we have used different combinations proposed by this page and all of them work, but we have detected we are not able to make play to also serve routing defined by our conf/routes.

We have found that we can extend the following code (provided at ScalaEmbeddingPlayAkkaHttp) to also serve static assets:

val _server = AkkaHttpServer.fromRouterWithComponents (serverConfig) { components =>
  import Results._
  import components.{ defaultActionBuilder => Action }
  {
    case GET(p"/hello/$to") => Action {
      Ok(views.html.index(to))
      /* Ok(s"Hello $to") */
    }
  }
}

…by using examples from https://github.com/lightbend/lightbend-markdown/blob/master/server/src/main/scala/com/lightbend/markdown/server/DocumentationServer.scala
(for example, using code like this):

  case GET(p"/$name/resources/$path*") if nameExists(name) => Action {
    sendFileInline(name, path).getOrElse(NotFound("Resource not found [" + path + "]"))
  }

However, support provided by conf/routes ( https://www.playframework.com/documentation/2.6.x/ScalaRouting ) is much more than that. It provides a lot of features (along with loading static assets).

In this context, we would like to be able embed Play Server with support for conf/routes defined.

Please, could you give us some advise how to do this?

Thanks for your time and patience.

[1] https://github.com/asples/reactive-myqtt


(Greg Methvin) #2

It should be possible to enable just the routes compiler plugin on your project, but it’s not documented anywhere I know of.

You’ll need to depend on the Play sbt plugin, so add to plugins.sbt:

addSbtPlugin("com.typesafe.play" %% "sbt-plugin" % "2.6.13")

Then add the RoutesCompiler plugin to your project, e.g.

lazy val root = (project in file(".")).enablePlugins(RoutesCompiler)

That won’t add any dependencies or other functionality from Play. It just adds the routes compiler task as a source generator for that project.

Then add sources for it to compile:

sources in (Compile, routes) ++= {
  val dirs = (unmanagedResourceDirectories in Compile).value
  (dirs * "routes").get ++ (dirs * "*.routes").get
}

That will set it up to compile any files named “routes” or ending in “.routes” in your unmanaged resource directories. So, for example, it would compile src/main/resources/routes or src/main/resources/com.example.foo.routes; in the former case the generated router class would be router.Routes and in the latter it would be com.example.foo.Routes.

Then you can construct your server like this:

object Example extends App {
  val components = new AkkaHttpServerComponents with BuiltInComponents {
    override lazy val serverConfig: ServerConfig = ServerConfig()
    override lazy val httpFilters = Nil
    override lazy val router = new Routes(httpErrorHandler, new MyController())
  }
  val server = components.server
}

You can mix traits into the components just like any other Play app using compile-time dependency injection.

Note that the static assets functionality is separate from Play’s routes compiler. The core play library comes with the Assets controller, which you can get by mixing in AssetsComponents. That serves resources from the /public directory on your classpath. You could use the assets controller from a SIRD router as well:

  case GET(p"/assets/$path*") => assets.at("/public", path)

I hope that helps. I think our documentation could definitely improved around how to use Play’s plugins individually without completely Play-ifying your project.


(Francis Brosnan Blázquez) #3

Hello @greg
Thanks for taking your time and all notes.
I’m going to check all this and let you know.
Best Regards