Unable to provision because of "No implementation for controllers.MyExecutionContext was bound" - scala

I'm trying to follow the instructions to create a Play Framework async controller. So far my code is little more than a cut & paste from the Play documentation:
package controllers
import akka.actor.ActorSystem
import com.google.inject.Inject
import play.api.libs.concurrent.CustomExecutionContext
import play.api.mvc.{AbstractController, ControllerComponents}
import scala.concurrent.{ExecutionContext, Future}
trait MyExecutionContext extends ExecutionContext
class MyExecutionContextImpl #Inject()(system: ActorSystem)
extends CustomExecutionContext(system, "my.executor") with MyExecutionContext
class FooController #Inject() (myExecutionContext: MyExecutionContext, cc:ControllerComponents) extends AbstractController(cc) {
def foo = Action.async(
Future {
// Call some blocking API
Ok("result of blocking call")
}(myExecutionContext)
)
}
When I try to run this new controller, I get the following error:
ProvisionException: Unable to provision, see the following errors:
1) No implementation for controllers.MyExecutionContext was bound.
while locating controllers.MyExecutionContext
for the 1st parameter of controllers.FooController.<init>(FooController.scala:14)
while locating controllers.FooController
for the 4th parameter of router.Routes.<init>(Routes.scala:33)
while locating router.Routes
while locating play.api.inject.RoutesProvider
Can anybody explain what might be going wrong here?

The exception indicates that you have not bound the implementation (MyExecutionContext) to the trait (MyExecutionContextImpl) in your Module.
Try this:
class Module extends AbstractModule {
override def configure(): Unit = {
bind(classOf[MyExecutionContext])
.to(classOf[MyExecutionContextImpl])
}
}
However I never used your approach. I use only the default Execution Context like:
class FooController #Inject()()(implicit val ec: ExecutionContext)

The previous answer does not work. Try instead using #ImplementedBy
(source: https://stackoverflow.com/a/53162884/4965515)
In addition you will need to configure your executor in conf/application.conf. For instance try adding:
my.executor {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
core-pool-size-factor = 10.0
core-pool-size-max = 10
}
}
(source https://stackoverflow.com/a/46013937/4965515)

Related

How to bind a class that extends a trait with a monadic type in Scala Guice

I've created simple Scala Play application that has traits with a monadic type and their respective implementation binded in Module configure as:
class Module extends AbstractModule {
override def configure() = {
bind(new TypeLiteral[UserDAO[DBIO]](){}).to(classOf[UserDAOImpl])
bind(new TypeLiteral[UserService[Future]](){}).to(classOf[UserServiceImpl[Future, DBIO]])
}
}
Those traits and implementations are:
///TRAITS
//UserDAO.scala
package models
trait UserDAO[DB[_]] {
def get(userId: Long): DB[Option[User]]
}
//UserService.scala
package services
import resources.UserResponse
import services.response.ServiceResponse
trait UserService[F[_]] {
def findUserById(id: Long): F[ServiceResponse[UserResponse]]
}
///IMPLEMENTATIONS
//UserDAOImpl.scala
package dao
import models.{DataContext, User, UserDAO}
import play.api.db.slick.DatabaseConfigProvider
import slick.dbio.{DBIO => SLICKDBIO}
import javax.inject.Inject
import scala.concurrent.ExecutionContext
class UserDAOImpl #Inject()(
protected val dbConfigProvider: DatabaseConfigProvider,
val context: DataContext
)(
implicit executionContext: ExecutionContext
) extends UserDAO[SLICKDBIO] {
import context.profile.api._
override def get(userId: Long): SLICKDBIO[Option[User]] = context.Users.filter(_.id === userId).result.headOption
}
//UserServiceImpl.scala
package services
import resources.Mappings.UserToResponseMapping
import cats.Monad
import cats.implicits._
import models.{DatabaseManager, UserDAO}
import resources.{NoModel, UserResponse}
import services.response.ServiceResponse
import util.Conversions.{errorToServiceResponse, objectToServiceResponse}
import javax.inject.Inject
class UserServiceImpl[F[_]: Monad, DB[_]: Monad]#Inject()(userRepo: UserDAO[DB],
dbManager: DatabaseManager[F, DB])
extends UserService[F] {
override def findUserById(id: Long): F[ServiceResponse[UserResponse]] = {
for {
user <- dbManager.execute(userRepo.get(id))
} yield user match {
case Some(user) =>user.asResponse.as200
case None => NoModel(id).as404
}
}
}
However, this fails to inject dependencies and throws the following errors:
play.api.UnexpectedException: Unexpected exception[CreationException: Unable to create injector, see the following errors:
1) models.UserDAO<DB> cannot be used as a key; It is not fully specified.
at services.UserServiceImpl.<init>(UserServiceImpl.scala:13)
at Module.configure(Module.scala:35) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
2) models.DatabaseManager<F, DB> cannot be used as a key; It is not fully specified.
at services.UserServiceImpl.<init>(UserServiceImpl.scala:13)
at Module.configure(Module.scala:35) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
3) cats.Monad<F> cannot be used as a key; It is not fully specified.
at services.UserServiceImpl.<init>(UserServiceImpl.scala:13)
at Module.configure(Module.scala:35) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
This question might be related to this one How to bind a class that extends a Trait with a monadic type parameter using Scala Guice?, and in my solution I've applied what is suggested as the answer, but it still fails.
Any suggestions?
If you look at the stacktrace, you can see the issue happens when Guice wants to create an instance of UserServiceImpl:
... at services.UserServiceImpl.<init> ...
I suspect that Guice cannot know what to "inject" when trying to create this class. It cannot infer that it has to inject a UserDao[DBIO] for instance, it only knows it has to inject a UserDao[DB] with DB being something unspecified.
How to fix that, I can't say for sure but I would look into either:
adding "concrete" class for UserServiceImpl and bind it instead of the generic one (like a class UserServiceFutureDBIO)
manually instantiating a UserServiceImpl and binding to an instance rather than binding to a class and letting Guice instantiate it

Scala Play run time injection based on request parameter

I am using Scala Play 2.6 and trying to use dependency injection to instantiate a service class based on request parameter. As below example code, the controller class get payment method from query string
package controllers
import com.google.inject.Inject
import play.api.mvc._
import scala.concurrent.ExecutionContext
class PaymentController #Inject()()
(implicit ec: ExecutionContext)
extends InjectedController {
def doPayment() = Action.async { implicit request =>
request.getQueryString("payment-method").getOrElse("") match {
case "paypal" => // Inject a PaypalPaymentService
val paymentService = Play.current.injector.instanceOf[PaypalPaymentService]
paymentService.processPayment()
case "creditcard" => // Inject a CreditCardPaymentService
val paymentService = Play.current.injector.instanceOf[CreditCardPaymentService]
paymentService.processPayment()
case _ => // Return error
}
}
}
And services class to process Paypal or CreditCard payment
package services
import scala.concurrent.Future
trait PaymentService {
def processPayment(): Future[Boolean]
}
package services
import com.google.inject.Inject
import scala.concurrent.{ExecutionContext, Future}
import play.api.libs.ws.WSClient
class PaypalPaymentService #Inject()(ws: WSClient)
(implicit ec: ExecutionContext)
extends PaymentService {
def processPayment(): Future[Boolean] = {
//Process paypal payment
}
}
class CreditCardPaymentService #Inject()(ws: WSClient)
(implicit ec: ExecutionContext)
extends PaymentService {
def processPayment(): Future[Boolean] = {
//Process credit card payment
}
}
For Play 2.5 onwards, Play.current and Play.application have been deprecated.
I have two questions:
Is the above example code a correct way to inject a class based on
request parameter? or is there some other better way to do so?
For Play 2.5/2.6, what is the way to get the application injector?
You have stated correctly that Play.current and Play.application have been deprecated and from 2.5 onwards the way to use them is indeed by injecting them.
I would change your controller definition so that you make use of DI to include the needed components. Something like:
class PaymentController #Inject()(configuration: Configuration)
(implicit ec: ExecutionContext) extends Controller {
// your code goes here
}
Now comes the tricky part. You might thing that it is possible just to inject application: play.Application but this is not entirely true because you are going to run into circular dependencies. This is normal because you want to inject the whole application while actually being in it. There is one hack for this and it is by injecting Provider[Application]. I call this a hack because normally you don't need/want to inject the whole application. In 99% of the cases you are interested only in specific parts - e.g. the Configuration, Environment, etc.
And here comes the solution. You can just inject your Injector
class PaymentController #Inject()(injector: Injector)
(implicit ec: ExecutionContext) extends Controller {
// your code goes here
}
From here the game is an easy one. Just use the Injector to get the needed services. Like this:
case "paypal" => // Inject a PaypalPaymentService
val paymentService = injector.instanceOf(classOf[PaypalPaymentService])
paymentService.processPayment()
Last words regarding the "correct way" of using this. I actually find your approach OK and would not necessarily change it. Just one idea in this direction is that you create a Module like this:
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class PaymentModule extends AbstractModule {
def configure() = {
bind(classOf[PaymentService])
.annotatedWith(Names.named("paypal"))
.to(classOf[PaypalPaymentService])
bind(classOf[PaymentService])
.annotatedWith(Names.named("creditcard"))
.to(classOf[CreditCardPaymentService])
}
}
Having a common trait (as you do it) helps in this case and you can have multiple implementations, even mocked ones for your tests. The module will be registered automatically if it lies in the root package. Otherwise you should tell Play the location of it:
play.modules.enabled += "modules.PaymentModule"

Can't inject actorSystem in service

I am trying to create a service that runs in the background of my app (reads and writes to a queue) that I want to build with the actor system. However I am getting an error when I try to inject an ActorSystem into my class:
play.api.UnexpectedException: Unexpected exception[CreationException: Unable to create injector, see the following errors:
1) Error injecting constructor, java.lang.IllegalArgumentException: no matching constructor found on class services.Indexer$IndexActor for arguments []
at services.Indexer.<init>(Indexer.scala:21)
at Module.configure(Module.scala:6) (via modules: com.google.inject.util.Modules$OverrideModule -> Module)
while locating services.Indexer
Here is my setup:
// Module.scala
class Module extends AbstractModule {
override def configure() = {
bind(classOf[Indexer]).asEagerSingleton() // I suspect this needs to change
}
}
// Indexer.scala
#Singleton
class Indexer #Inject() (appLifecycle: ApplicationLifecycle, system: ActorSystem) (implicit ec: ExecutionContext) { ... }
In the play documentation there is an example of injecting an actor system, but that only seems to work when you inject into a class which extends Controller:
#Singleton
class MyController #Inject()(system: ActorSystem)(implicit exec: ExecutionContext) extends Controller {
// This works
}
Found the problem:
In Module.scala I am using the com.google.inject package
import com.google.inject.AbstractModule // <-------
import services.Indexer
class Module extends AbstractModule {
override def configure() = {
bind(classOf[Indexer]).asEagerSingleton()
}
}
But in my Indexer.scala service I was using the javax.inject package.
I have switched all of my files to now:
import com.google.inject._

Failed on Injecting Akka Actor to Play Framework 2.5 with Guice

I am trying to use an Akka actor in my Play 2.5 application with dependency injection. I basically followed the documentation on that, and here's a glimpse of my code:
The Actor
Basically, I have a simple actor that receive some data, process it, and send it back to the caller.
package actors
import akka.actor._
import akka.pattern.{AskTimeoutException, ask}
import akka.util.Timeout
import javax.inject._
import com.google.inject.assistedinject.Assisted
import play.api.Configuration
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
object ServiceActor {
val ConfigKey = "actors.path" // remote actor path
trait Factory {
def apply(key: String): Actor
}
}
class ServiceActor #Inject()(configuration: Configuration,
actorSystem: ActorSystem,
#Assisted key: String)
(implicit val ec: ExecutionContext)
extends Actor {
import ServiceActor._
implicit val timeout = Timeout(5.minutes)
def receive = {
case data: SomeData => sender ! someFunction(data)
}
// implemented in the real code, omitted because not important for the question
def someFunction = ???
}
The Module
As per documentation, I need to make a simple module, and activate it. Here's the module:
package modules
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
import actors.ServiceActor
class ServiceModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[ServiceActor]("service-actor")
}
}
And then I activate this module by adding the following line to application.conf:
play.modules {
enabled += modules.ServiceModule
}
The Controller
Finally, I try to use the actor in a controller:
package controllers
import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import javax.inject._
import play.api.mvc._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
#Singleton
class SearchController #Inject()(#Named("service-actor") serviceActor: ActorRef)
(implicit ec: ExecutionContext)
extends Controller {
implicit val timeout = Timeout(5.minutes)
def search(param: String) = Action.async {
val request = SomeRequestType(param)
(serviceActor ? request).mapTo[SomeResponseType] map { result =>
Ok(result.toString)
}
}
}
As far as I can understand, I have followed the documentation perfectly, but calling the controller give me this error:
[AskTimeoutException: Recipient[Actor[akka://application/user/service-actor#-366470383]]
had already been terminated.
Sender[null] sent the message of type "some.namespace.SomeRequestType".]
As far as I can understand, the error is about how the actor is a null, and we can't send message to it. While the error code said "had already been terminated", it looks like the actor's never been initiated in the first place.
I'm not sure where to look first, and what am I doing wrong as I have no experience in using Guice.
The question is, what am I doing wrong? Why didn't the actor initiated, or if it is indeed already terminated, why is it terminated? How do I fix this?
Thank you very much.
Hope you have already solved the issue. However I am still posting answers here just for some new comers' reference since it took me a while to figure out the cause of this issue.
As far as I can understand, the error is about how the actor is a null, and we can't send message to it.
Not exactly. Ask pattern ? has an implicit parameter of sender, which defaults to be ActorRef.noSender, the source code is here.
Normally, if you're inside an Actor you have an implicit ActorRef in scope called self, but since you're not in an Actor it's just taking the default.
While the error code said "had already been terminated", it looks like the actor's never been initiated in the first place.
Correct. Most likely your actor was never initiated correctly in the first place. In your console, you should see more error messages than what you are seeing from the response of Play Webpage.
For example below is the error I actually got before I got the AskTimeoutException.
1 error
akka.actor.ActorInitializationException: akka://application/user/user-actor: exception during creation
at akka.actor.ActorInitializationException$.apply(Actor.scala:193)
at akka.actor.ActorCell.create(ActorCell.scala:608)
at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:462)
at akka.actor.ActorCell.systemInvoke(ActorCell.scala:484)
at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:282)
at akka.dispatch.Mailbox.run(Mailbox.scala:223)
at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
Caused by: com.google.inject.ConfigurationException: Guice configuration errors:
To wrap up, you may want to check the sbt console again and also your injection to make sure the actor was initiated in the first place.

Why are implicit variables not initialized in Scala when called from unit test?

Given the the following singleton Object in Scala:
package demo
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.concurrent.Future
import scala.io.StdIn
object WebServer extends App {
implicit val system = ActorSystem("myActorSystem")
implicit val executionContext = system.dispatcher
implicit val materializer = ActorMaterializer()
val route = {
path("api" / "done-as-promised") {
get {
complete {
Future.successful("done")
}
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
And the following unit test
package demo
import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.scalactic.TypeCheckedTripleEquals
import org.scalatest.{Inspectors, Matchers, WordSpec}
class WebServerSpec extends WordSpec with Matchers with TypeCheckedTripleEquals with Inspectors with ScalatestRouteTest {
"The WebServer /done-as-promised" should {
"return done" in {
// tests:
Get("/api/done-as-promised") ~> WebServer.route ~> check {
status.intValue() shouldEqual 200
responseAs[String] shouldEqual "done"
}
}
}
}
I get the following error:
[ERROR] [04/19/2016 07:12:18.995]
[ScalaTest-run-running-WebServerSpec]
[akka.actor.ActorSystemImpl(demo-WebServerSpec)] Error during
processing of request
HttpRequest(HttpMethod(GET),http://example.com/api/done-as-promised,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1))
java.lang.NullPointerException at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:33)
at
akka.http.scaladsl.server.directives.ExecutionDirectives$$anonfun$handleExceptions$1$$anonfun$apply$1.apply(ExecutionDirectives.scala:29)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:162)
at
akka.http.scaladsl.testkit.RouteTest$TildeArrow$$anon$1.apply(RouteTest.scala:150)
It took me a while to figure out. The thing is: removing the extends app will make the test succeed.
The reason for the problem is that when WebServer is declared as extends App, it uses the DelayedInit functionality of the App trait. Because of this, the initialization code in the contructor is not added to the constructor of the WebServer object. Instead is called when the main method is called on the WebServer. So when he references the "route" inside the tests, those are all coming up null.
Mixing in the DelayedInit trait (App extends from DelayedInit) will rewrite your class or object template. Instead of adding your val's and var's to the constructor, it will be added to the def delayedInit(body: => Unit) hook (inaccessible to user code). Apparently this one is called whenever the main method is called.
You can verify this by simply calling "main" on the WebServer inside the test. If you do this, then the test will pass. This is because calling main triggers the initialization resulting in those objects being created.
Generally speaking though the right solution is probably to move the routing to somewhere else, rather than having it inside of the base App.