Can't inject actorSystem in service - scala

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._

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

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

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)

How do I access the actor system used in Play 2.5 from within a Module

I need access to the default actor system that Play Framework 2.5 uses from within my Module class.
I see that there is a method on ActorSystemProvider to get this:
#Singleton
class ActorSystemProvider #Inject()(environment: Environment, configuration: Configuration, applicationLifecycle: ApplicationLifecycle) extends Provider[ActorSystem] {
private val logger = Logger(classOf[ActorSystemProvider])
lazy val get: ActorSystem = {
val (system, stopHook) = ActorSystemProvider.start(environment.classLoader, configuration)
applicationLifecycle.addStopHook(stopHook)
system
}
}
But how do I get access to this class in my Module class?
For example:
class Module extends AbstractModule {
val playSystem: ActorSytem = ???
...
}
You can access actorSystem by simply injecting it into any of the component constructor. You will get access to the actorSystem created by play and you need not do any of the provider gymnastics.
For example, I need actor system to be accessible in my HomeController. So, I just inject into my HomeController constructor.
class HomeController #Inject() (actorSystem: ActorSystem) extends Controller {
def index = Ok("bye!")
}

Dependency Injection to Play Framework 2.5 modules

I have a module class with the following signature:
class SilhouetteModule extends AbstractModule with ScalaModule {
I would like to inject configuration:
class SilhouetteModule #Inject() (configuration: Configuration) extends AbstractModule with ScalaModule {
But it fails with the following error.
No valid constructors
Module [modules.SilhouetteModule] cannot be instantiated.
The Play documentation mentions that
In most cases, if you need to access Configuration when you create a component, you should inject the Configuration object into the component itself or...
, but I can't figure out how to do it successfully. So the question is, how do I inject a dependency into a module class in Play 2.5?
There are two solutions to solve your problem.
First one (and the more straight forward one):
Do not extend the com.google.inject.AbstractModule. Instead use the play.api.inject.Module. Extending that forces you to override def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]]. Within that method you could do all your bindings and you get the configuration inserted as a method-parameter.
Second one (and the more flexible one):
Depending on your needs of the components you want to inject, you could define a provider for the component you want to bind. In that provider you could inject whatever you want. E.g.
import com.google.inject.Provider
class MyComponentProvider #Inject()(configuration:Configuration) extends Provider[MyComponent] {
override def get(): MyComponent = {
//do what ever you like to do with the configuration
// return an instance of MyComponent
}
}
Then you could bind your component within your module:
class SilhouetteModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[MyComponent]).toProvider(classOf[MyComponentProvider])
}
}
The advantage of the second version, is that you are able to inject what ever you like. In the first version you get "just" the configuration.
Change your constructor signature from:
class SilhouetteModule #Inject() (configuration: Configuration) extends AbstractModule with ScalaModule
to:
class SilhouetteModule(env: Environment, configuration: Configuration) extends AbstractModule with ScalaModule
see here for more info:
https://github.com/playframework/playframework/issues/8474

Eagerly initialize singleton actor in scalaguice

I would like to eagerly initialize a singleton actor. I currently do the below and then later in my app startup get the instance of the actor.
`bind[Actor].annotatedWith(Names.named(LockCoordinator.name)).to[LockCoordinator].in[Singleton]`
I have tried
bind[Actor].annotatedWith(Names.named(LockCoordinator.name)).to[LockCoordinator].asEagerSingleton()
but fails at runtime with
1) Error injecting constructor, akka.actor.ActorInitializationException: You cannot create an instance of [LockCoordinator] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.
I do create an eagerSingleton already for the system, but couldn't figure out how to apply it for an Actor (not ActorRef)
class ActorSystemProvider #Inject() (val config: Config, val injector: Injector) extends Provider[ActorSystem] {
override def get() = {
val system = ActorSystem(config.getString("mysystem"), config)
GuiceAkkaExtension(system).initialize(injector)
system
}
}
Is there a boilerplate free way of achieving this? As I want to apply this to 3-4 other actors
Does this help?
https://gist.github.com/fancellu/e4e8acdc3d7fd3b9d749352f9d6c68e3
import actors.ActorBrowserActor
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
class Module extends AbstractModule with AkkaGuiceSupport{
def configure(): Unit = {
bindActor[ActorBrowserActor](ActorBrowserActor.NAME, _=>ActorBrowserActor.props)
}
}
here we inject the actor by name
class SampleController #Inject()(implicit system: ActorSystem, val messagesApi: MessagesApi, #Named("actor-browser-actor") actorBrowserActor: ActorRef)
extends Controller with I18nSupport{
}