I am following the DB presription provided here. However, the class DatabaseExecutionContext is nowhere near to be found in the play API and, hence, I am unable to import it.
What am I missing?
See the example code here: https://github.com/playframework/play-samples/blob/2.8.x/play-scala-anorm-example/app/models/DatabaseExecutionContext.scala
import javax.inject._
import akka.actor.ActorSystem
import play.api.libs.concurrent.CustomExecutionContext
#Singleton
class DatabaseExecutionContext #Inject()(system: ActorSystem) extends CustomExecutionContext(system, "database.dispatcher")
You need to configure this context as shown by the documentation: https://www.playframework.com/documentation/2.8.x/AccessingAnSQLDatabase#Using-a-CustomExecutionContext
While you already got an answer, I suggest to use a base trait instead, like:
import javax.inject.{Inject, Singleton}
import akka.actor.ActorSystem
import play.api.libs.concurrent.CustomExecutionContext
import scala.concurrent.ExecutionContext
trait DatabaseExecutionContext extends ExecutionContext
#Singleton
class DatabaseAkkaExecutionContext #Inject()(system: ActorSystem)
extends CustomExecutionContext(system, "database.dispatcher")
with DatabaseExecutionContext
The reason is that if you don't, you'll need to bring akka while testing operations requiring this execution context, with the trait, you should be able to write a simple executor for your tests, like:
implicit val globalEC: ExecutionContext = scala.concurrent.ExecutionContext.global
implicit val databaseEC: DatabaseExecutionContext = new DatabaseExecutionContext {
override def execute(runnable: Runnable): Unit = globalEC.execute(runnable)
override def reportFailure(cause: Throwable): Unit = globalEC.reportFailure(cause)
}
EDIT: I have created a detailed post explaining this.
Related
I want to start testcontainers from a docker-compose file (postgres and kafka instance), before the play application (with slick) starts up. I want this, so I can write a end to end test. I can not seem to figure out how this is possible with Play.
import java.io.File
import com.dimafeng.testcontainers.DockerComposeContainer.ComposeFile
import com.dimafeng.testcontainers.{DockerComposeContainer, ForAllTestContainer}
import com.typesafe.config.ConfigFactory
import org.scalatest.{BeforeAndAfterAll, FunSpec}
import org.scalatestplus.play.guice.GuiceFakeApplicationFactory
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.{Application, Configuration, Environment, Mode}
trait TestFunSpec extends FunSpec with BeforeAndAfterAll with GuiceFakeApplicationFactory {
override def fakeApplication(): Application = new GuiceApplicationBuilder()
.in(Environment(new File("."), getClass.getClassLoader, Mode.Test))
.loadConfig(_ => Configuration(ConfigFactory.load("test.conf")))
.build
}
class TestIntegrationSpec extends TestFunSpec with ForAllTestContainer {
override val container = DockerComposeContainer(ComposeFile(Left(new File("docker-compose.yml"))))
it("should test something") {
assert(true)
}
}
Scala version 2.12.10
Testcontainer version 0.35.0
Play slick version 5.0.0
When I execute the test without "TestFunSpec", the docker-compose spins up my services correctly. When I add the play application "TestFunSpec" in scope, the application tries to startup, when doing so, it tries to connect with postgres, which is not yet existing (as the testcontainers are started afterwards).
Thnx in advance.
Update: see answer section for an elaborate answer.
After some deep dive in the Play test suite mechanism, I came up with a workable setup.
Step 1, define your test container suite with an AppProvider:
import com.dimafeng.testcontainers.{Container, ForAllTestContainer}
import org.scalatest.Suite
import org.scalatestplus.play.AppProvider
trait PlayTestContainer extends Suite with AppProvider with ForAllTestContainer {
override val container: Container
}
Step 2, create an abstract PlayTestContainerIntegrationSpec which extends the above trait:
import java.io.File
import com.typesafe.config.ConfigFactory
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, TestData}
import org.scalatestplus.play.PlaySpec
import org.scalatestplus.play.guice.GuiceOneAppPerTest
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.{Application, Configuration, Environment, Mode}
abstract class PlayTestContainerIntegrationSpec
extends PlaySpec
with PlayTestContainer
with GuiceOneAppPerTest
with ScalaFutures
with IntegrationPatience
with BeforeAndAfterEach
with BeforeAndAfterAll {
override def newAppForTest(testData: TestData): Application = application()
def application(): Application =
new GuiceApplicationBuilder()
.in(Environment(new File("."), getClass.getClassLoader, Mode.Test))
.loadConfig(_ => Configuration(ConfigFactory.load("test.conf")))
.build
}
As you can see, we include the "PlayTestContainer" trait and we override the "newAppForTest" function for the construction of a play application.
Step 3, create a specific integration test, extend the above abstract PlayTestContainerIntegrationSpec, and override the container, to your specific need:
class TestIntegrationSpec extends PlayTestContainerIntegrationSpec {
override val container = DockerComposeContainer(ComposeFile(Left(new File("docker-compose.yml"))))
"should test something" in {
assert(true === true)
}
}
Hope this helps.
I hate to ask - I really do but this one has got me for the moment..
I'm trying to compose some actions (in Play Framework & scala) with my main guide being this vid. However it was made a few years back so some of the functionality has since been deprecated and therefore I have had to find work-arounds as I go. Currently I am trying to output two asynchronous actions within some HTML markup.
I successfully outputted one action with this controller:
package controllers
import akka.actor.ActorSystem
import javax.inject._
import play.api.mvc._
import services.ServiceClient
import scala.concurrent.ExecutionContext
#Singleton
class AsyncController #Inject() (sc: ServiceClient)(actorSystem: ActorSystem)(implicit exec: ExecutionContext) extends Controller {
def index = Action.async { request =>
val asy1 = sc.makeServiceCall("async1")
for {
async1Message <- asy1
} yield {
Ok(views.html.async1.async1(async1Message))
}
}
}
In case you are wondering the sc.makeServiceCall refers to this file:
class ServiceClient #Inject() (ws: WSClient) {
def makeServiceCall(serviceName: String): Future[String] = {
ws.url(s"http://localhost:9000/mock/$serviceName").get().map(_.body)
}
}
So I followed the video in its' guidance to compose two asynchronous actions with some HTML. And this is where it gets difficult/interesting/upsetting:
package controllers
import javax.inject.Inject
import akka.actor.ActorSystem
import play.api.mvc._
import scala.concurrent.{ExecutionContext}
import Ui.Pagelet
class AsyncHomeController #Inject() (as1: AsyncController)(as2: Async2Controller)(actorSystem: ActorSystem)(implicit exec: ExecutionContext) extends Controller {
def index = Action.async { request =>
val asy1 = as1.index(request)
val asy2 = as2.index(request)
for {
async1Result <- asy1
async2Result <- asy2
async1Body <- Pagelet.readBody(async1Result)
async2Body <- Pagelet.readBody(async2Result)
} yield {
Ok(views.html.home2(async1Body, async2Body))
}
}
}
So Async2Controller is very similar to AsyncController and Pagelet.readBody refers to this:
package Ui
import play.api.libs.iteratee.Iteratee
import play.api.mvc.{Codec, Result}
import play.twirl.api.Html
import scala.concurrent._
object Pagelet {
def readBody(result: Result)(implicit codec: Codec): Future[Html] = {
result.body.run(Iteratee.consume()).map(bytes => Html(new String(bytes, codec.charset)))
}
}
And this is wherein the error lies - which is:
value run is not a member of play.api.http.HttpEntity
I cannot find documentation on whether it needs to be injected or any indication that it has since been deprecated. If someone has got an answer to this or a work-around please divulge. Many thanks
The Iteratee lib is deprecated and was replaced by akka-stream. You need to change the implementation of readBody:
def readBody(result: Result)(implicit mat: Materializer, ec: ExecutionContext, codec: Codec): Future[Html] = {
result.body.consumeData.map(byteString => Html(codec.decode(byteString))
}
You also need to change the dependencies of the controller to get a Materializer:
class AsyncHomeController #Inject() (as1: AsyncController, as2: Async2Controller)(actorSystem: ActorSystem)(implicit exec: ExecutionContext, mat: Materializer)
Edit: code updated
I have a play-scala application using SqLite with slick. My tables are defined like this:
#Singleton
class DataSets #Inject()(protected val dbConfigProvider: DatabaseConfigProvider, keys: PublicKeys) extends DataSetsComponent
with HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
val DataSets = TableQuery[DataSetsTable]
def all = db.run(DataSets.sortBy { _.id }.result)
...
}
My controllers get access via DI:
#Singleton
class DataSetsController #Inject() (dataSets: DataSets, env: play.Environment) extends Controller {
...
How do I get a database handle in an Actor?
class TrainActor #Inject() (dataSets: DataSets) extends Actor {
...
of course does not work as Guice does not find the DataSets class.
Edit: to clarify: I do not want to use the actor for database access in the controller (via ask), but to start some resource intensive computations from the controller after a request and store them in the db afterwards (async).
I now found a way which integrates with DI, closely following the official documentation. Because for the need of an ActorContext, InjectedActorSupport can only be inherited by Actors. This means I had to create an actor which does nothing than instantiate and start new "worker" Actors. Maybe there is an easier way, but this works correctly.
TrainActor.scala:
package actors
import javax.inject.Inject
import akka.actor._
import com.google.inject.assistedinject.Assisted
import models.{DataSet, Model, PublicKey}
import play.api.Logger
import tables.DataSets
import scala.concurrent.ExecutionContext.Implicits.global
object TrainActor {
case object Start
case class LoadData(d: DataSet, k: PublicKey)
trait Factory {
def apply(model: Model): Actor
}
}
class TrainActor #Inject() (val dataSets: DataSets, #Assisted val model: Model) extends Actor {
import TrainActor._
def receive = {
case Start =>
dataSets.findWithKey(model.id.get)
...
TrainActorStarter.scala:
package actors
import javax.inject.Inject
import akka.actor.{Actor, ActorRef}
import models.Model
import play.api.libs.concurrent.InjectedActorSupport
object TrainActorStarter {
case class StartTraining(model: Model)
}
/**
* https://www.playframework.com/documentation/2.5.x/ScalaAkka#Dependency-injecting-actors
* #param childFactory
*/
class TrainActorStarter #Inject() (childFactory: TrainActor.Factory) extends Actor with InjectedActorSupport {
import TrainActorStarter._
def receive = {
case StartTraining(model: Model) =>
val trainer: ActorRef = injectedChild(childFactory(model), s"train-model-model-${model.id.get}")
trainer ! TrainActor.Start
}
}
ActorModule.scala:
package actors
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class ActorModule extends AbstractModule with AkkaGuiceSupport {
def configure(): Unit = {
bindActor[TrainActorStarter]("train-actor-starter")
bindActorFactory[TrainActor, TrainActor.Factory]
}
}
And finally in the controller:
package controllers
import javax.inject._
import actors.{TrainActorStarter, TrainCallbackActor}
import akka.actor.{ActorRef, ActorSystem, _}
import akka.stream.Materializer
...
#Singleton
class ModelsController #Inject() (implicit system: ActorSystem, materializer: Materializer, ..., #Named("train-actor-starter") trainActorStarter: ActorRef) extends Controller with InjectedActorSupport {
def startTraining(model: Model): Unit = {
if(model.id.isEmpty) return
trainActorStarter ! TrainActorStarter.StartTraining(model)
}
You can inject dependencies into an actor:
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class MyModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[TrainActor]("injected-train-actor")
}
}
After that just inject actor into controller:
class MyController #Inject()(#Named("injected-train-actor") trainActor: ActorRef) {
def endpointTest = Action.async {
for {
items <- (trainActor ? FetchAll).mapTo[Seq[DataSetsTableRow]]
} yield Ok(Json.toJson(items))
}
}
Instead of having
#Singleton
class DataSets
one can declare it as a simple scala object that can act as the DataSetsDAO
object DataSets
and then in the actor just use DataSets.dbOperation just bear in mind that the result type of that will be a Future, so just schedule a message to self in the actor on the onComplete to avoid any side effects.
I am trying to replicate the basic example proposed in the Integrating with Akka, Play 2.4 for Scala doc. But I have difficulties in placing the final pieces together...
I have defined the actor (see paragraph Writing actors) at app/actors/HelloActor.scala with the following code:
package actors
import akka.actor._
object HelloActor {
def props = Props[HelloActor]
case class SayHello(name: String)
}
class HelloActor extends Actor {
import HelloActor._
def receive = {
case SayHello(name: String) =>
sender() ! "Hello, " + name
}
}
Then (see Creating and using actors) I suppose I should create a controller at app/controllers/Hello.scala with something like:
package controllers
import play.api.mvc._
import akka.actor._
import javax.inject._
import actors.HelloActor
#Singleton
class Hello #Inject() (system: ActorSystem) extends Controller {
val helloActor = system.actorOf(HelloActor.props, "hello-actor")
...
}
The question: where and how I utilize the code in the following paragraph Asking things of actors to have a working solution? I have tried to add it to the above Hello.scala controller but without success.
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
import akka.pattern.ask
implicit val timeout = 5.seconds
def sayHello(name: String) = Action.async {
(helloActor ? SayHello(name)).mapTo[String].map { message =>
Ok(message)
}
}
Found the solution, I had some problems with defining the implicit timeout, this is the working controller:
package controllers
import play.api.mvc._
import akka.actor._
import javax.inject._
import actors.HelloActor
import actors.HelloActor.SayHello
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
import akka.pattern.ask
import akka.util.Timeout
#Singleton
class Hello #Inject() (system: ActorSystem) extends Controller {
val helloActor = system.actorOf(HelloActor.props, "hello-actor")
implicit val timeout: Timeout = 5.seconds
def sayHello(name: String) = Action.async {
(helloActor ? SayHello(name)).mapTo[String].map { message ⇒
Ok(message)
}
}
}
Plus I added the following route in app/conf/routes:
# Actor test
GET /hello/:name controllers.Hello.sayHello(name)
MyService.scala:33: could not find implicit value for parameter eh: spray.routing.ExceptionHandler
I have run into a "missing implicit" compilation error using Akka, in spray.io code that makes an http call to a separate back-end server, as part of responding to an http get. The code needs to import quite a lot of Spray and Akka libraries, so it's a bit hard figuring whether there may be some library conflicts causing this, and I'd rather figure how to logically trace this sort of problem for this and other cases.
The missing implicit is encountered on calling runRoute(myRoute)
Here's the code:
import spray.routing._
import akka.actor.Actor
import akka.actor.ActorSystem
import spray.http._
import MediaTypes._
import akka.io.IO
import spray.httpx.RequestBuilding._
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import akka.util.Timeout
import HttpMethods._
import akka.pattern.ask
import akka.event.Logging
import scala.concurrent.duration._
// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class MyServiceActor extends Actor with MyService with akka.actor.ActorLogging {
log.info("Starting")
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute)
}
// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService {
implicit val system: ActorSystem = ActorSystem()
implicit val timeout: Timeout = Timeout(15.seconds)
import system.dispatcher // implicit execution context
//val logger = context.actorSelection("/user/logger")
val logger = actorRefFactory.actorSelection("../logger")
val myRoute =
{
def forward(): String = {
logger ! Log("forwarding to backend")
val response: Future[HttpResponse] =
(IO(Http) ? Get("http:3080//localhost/backend")).mapTo[HttpResponse]
"<html><body><h1>api response after backend processing</h1></body></html>"
}
path("") {
get {
respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here
complete(forward)
}
}
}
}
}
I am wondering what's the best way to solve this, hopefully providing insight into how to solve similar problems with implicits being missing, as they are somehow inherently not straightforward to track down.
EDIT: when trying to directly pass implicits as in #christian's answer below, I get:
MyService.scala:35: ambiguous implicit values:
both value context in trait Actor of type => akka.actor.ActorContext
and value system in trait MyService of type => akka.actor.ActorSystem
match expected type akka.actor.ActorRefFactory
RoutingSettings.default, LoggingContext.fromActorRefFactory)
^
Not quite sure why being specific as in #christian's answer leaves room for ambiguity for the compiler...
I ran into the same "could not find implicit value for parameter eh: spray.routing.ExceptionHandler" error earlier today. I tried #Christian's approach but saw a few "implicit values for xxx" creeping up. After scouting the error message a little I found adding implicit val system = context.system to the actor that runRoute solved the problem.
runRoute expects a few implicits. You are missing an import:
import spray.routing.RejectionHandler.Default
Update:
I think we also did have some problems with runRoute because we are supplying the implicit parameters explicitly:
runRoute(route)(ExceptionHandler.default, RejectionHandler.Default, context,
RoutingSettings.default, LoggingContext.fromActorRefFactory)
Update2:
To fix the last error, remove the creation of the ActorSystem (in MyService you get the actor system from MyServiceActor - therefore you have to use a self type annotation). This compiles:
import akka.actor.Actor
import akka.io.IO
import spray.httpx.RequestBuilding._
import spray.http.MediaTypes._
import spray.routing.{RoutingSettings, RejectionHandler, ExceptionHandler, HttpService}
import spray.util.LoggingContext
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import akka.util.Timeout
import HttpMethods._
import akka.pattern.ask
import akka.event.Logging
import scala.concurrent.duration._
// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class MyServiceActor extends Actor with MyService with akka.actor.ActorLogging {
log.info("Starting")
// the HttpService trait defines only one abstract member, which
// connects the services environment to the enclosing actor or test
implicit def actorRefFactory = context
// this actor only runs our route, but you could add
// other things here, like request stream processing
// or timeout handling
def receive = runRoute(myRoute)(ExceptionHandler.default, RejectionHandler.Default, context,
RoutingSettings.default, LoggingContext.fromActorRefFactory)
}
// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService { this: MyServiceActor =>
implicit val timeout: Timeout = Timeout(15.seconds)
implicit val system = context.system
//val logger = context.actorSelection("/user/logger")
val logger = actorRefFactory.actorSelection("../logger")
val myRoute =
{
def forward(): String = {
//logger ! Log("forwarding to backend")
val response: Future[HttpResponse] =
(IO(Http) ? Get("http:3080//localhost/backend")).mapTo[HttpResponse]
"<html><body><h1>api response after backend processing</h1></body></html>"
}
path("") {
get {
respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here
complete(forward)
}
}
}
}
}