How to have a Scala standalone application that uses the playframework libraries
Asked Answered
S

3

9

I have this simple test Scala application, which a blocking http request:

build.sbt

name := "hello"

version := "1.0"

scalaVersion := "2.11.2"

libraryDependencies += "com.typesafe.play" %% "play-ws" % "2.4.0-M1"

Test.scala

import play.api.libs.json._
import play.api.libs.ws._
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}

object Test {
  def main(args: Array[String]) = {
    val wsClient = WS.client
    val body = getBody(wsClient.url("http://example.com/").get())
    println(s"body: $body")
  }

  def getBody(future: Future[WSResponse]) = {
    val response = Await.result(future, Duration.Inf);
    if (response.status != 200)
      throw new Exception(response.statusText);
    response.body
  }
}

This application fails with:

Exception in thread "main" java.lang.RuntimeException: There is no started application

How to solve this issue?

Shekinah answered 6/10, 2014 at 16:14 Comment(0)
T
13

EDIT for Play 2.5:

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import play.api.libs.ws._
import play.api.libs.ws.ahc.AhcWSClient

import scala.concurrent.Future

object Main {
  import scala.concurrent.ExecutionContext.Implicits._

  def main(args: Array[String]): Unit = {
    implicit val system = ActorSystem()
    implicit val materializer = ActorMaterializer()
    val wsClient = AhcWSClient()

    call(wsClient)
      .andThen { case _ => wsClient.close() }
      .andThen { case _ => system.terminate() }
  }

  def call(wsClient: WSClient): Future[Unit] = {
    wsClient.url("http://www.google.com").get().map { response =>
      val statusText: String = response.statusText
      println(s"Got a response $statusText")
    }
  }
}

Please see:

for more detailed examples of standalone WSClient usage. If you are migrating from earlier versions, see https://www.playframework.com/documentation/2.5.x/Migration25#Play-WS-upgrades-to-AsyncHttpClient-2

For Play 2.4:

Do not use raw AsyncHttpClientConfig.Builder for HTTPS -- it does not configure a secure SSLContext with hostname validation.

You can create a new WSClient instance using the following code:

import play.api.libs.ws.ning._
import play.api.libs.ws._

val config = new NingAsyncHttpClientConfigBuilder(DefaultWSClientConfig()).build()
val builder = new AsyncHttpClientConfig.Builder(config)
val wsClient:WSClient = new NingWSClient(builder.build())

Please note that this will start up threads which will not be closed until you close the client:

wsClient.underlying[NingWSClient].close()

and you may run into memory leaks if you don't close it.

Tremulant answered 8/10, 2014 at 21:29 Comment(7)
The clearest example of how to do this that I've seen, thanks Will.Mcgean
all is deprecated now in 2.5.x.Countershading
Hi @Countershading -- please see playframework.com/documentation/2.5.x/ScalaWS#using-wsclient and playframework.com/documentation/2.5.x/…Tremulant
When I run this with play 2.4.8, the app doesn't exit. Some other thread is still alive. Is there another cleanup step?Klein
You need to call wsClient.close() after you're done.Tremulant
Here is a version for Play 2.3: gist.github.com/ScalaWilliam/39d9fc69204a9428b95cHetzel
I had threads sticking around even after wsClient.close(), but that turned out to be because I used the implicit Play execution context (for processing the futures returned from WS). Changing that to the Scala global one fixed it.Hetzel
B
8

Play 2.4 makes it very easy to utilize WS in a standalone app.

The following gist provides a nice working example and the following blog post provides a nice explanation.

Here are the highlights.

Configure build.sbt

libraryDependencies ++= Seq(
  "com.typesafe.play" %% "play-ws" % "2.4.0-M2"
)

Initialize the WS client

val config = new NingAsyncHttpClientConfigBuilder(DefaultWSClientConfig()).build
val builder = new AsyncHttpClientConfig.Builder(config)
val client = new NingWSClient(builder.build)

Use WS

client.url("http://www.example.com").get

Release WS resources

client.close()
Bawbee answered 2/2, 2015 at 23:43 Comment(0)
G
4

A started PlayApplication contains a client instance, which WS.client simply points to it. Since you won't start a Play application, You have to create your own client, like this:

val client = {
  val builder = new com.ning.http.client.AsyncHttpClientConfig.Builder()
  new play.api.libs.ws.ning.NingWSClient(builder.build())
}
client.url("http://example.com/").get()

Have a look on my project for a similar usecase, I am using play-ws and play-json, without Play itself.

Galenic answered 6/10, 2014 at 16:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.