'ambiguous implicit values' when injecting two actors into Play controller - scala

I want to inject two actors into a Play controller via DI. Injecting one actors works absolutely fine and I can send message to this actor without any problems. However, when injecting a second actor and sending a message, I get the following compilation error:
play.sbt.PlayExceptions$CompilationException: Compilation error[ambiguous implicit values:
both value fooSupervisor in class MyController of type akka.actor.ActorRef
and value barSupervisor in class MyController of type akka.actor.ActorRef
match expected type akka.actor.ActorRef]
My controller looks like this:
class MyController #Inject()(
implicit system: ActorSystem,
materializer: Materializer,
#Named("FooSupervisor") fooSupervisor: ActorRef,
#Named("BarSupervisor") barSupervisor: ActorRef
) extends Controller {
}
And the module looks as follows:
class Module extends AbstractModule with ScalaModule with AkkaGuiceSupport {
def configure() {
// ....
bindActor[FooSupervisor]("FooSupervisor")
bindActor[BarSupervisor]("BarSupervisor")
()
}
}
How can I solve this issue?

You define the two actors as implicit parameters. Change the signature like so:
class MyController #Inject()(
#Named("FooSupervisor") fooSupervisor: ActorRef,
#Named("BarSupervisor") barSupervisor: ActorRef
)(implicit system: ActorSystem, materializer: Materializer
) extends Controller {

I tried injecting the actorRef into the controller from a provider.
import javax.inject._
import akka.actor.ActorRef
import play.api.mvc._
#Singleton
class HomeController #Inject()(#Named("actor1") val actor1: ActorRef, #Named("actor2") val actor2: ActorRef) extends Controller {
def index = Action {
actor1 ! "hello actor1"
actor2 ! "hello actor2"
Ok("Hello")
}
}
The Module should bind the actorRef.
import javax.inject.{Inject, Provider}
import com.google.inject.AbstractModule
import net.codingwell.scalaguice.ScalaModule
import actors.{Actor1, Actor2}
import akka.actor.{ActorRef, ActorSystem, Props}
class Module extends AbstractModule with ScalaModule{
override def configure() = {
bind[ActorRef].annotatedWithName("actor1").toProvider[Actor1Provider]
bind[ActorRef].annotatedWithName("actor2").toProvider[Actor2Provider]
}
}
class Actor1Provider #Inject() (system: ActorSystem) extends Provider[ActorRef]{
override def get(): ActorRef = system.actorOf(Props[Actor1])
}
class Actor2Provider #Inject() (system: ActorSystem) extends Provider[ActorRef]{
override def get(): ActorRef = system.actorOf(Props[Actor2])
}
If you need a materializer and actorSystem please pass them as implicit parameters. Since we have injected the actorRef itself, we might not need a actorSystem to interact with the actors. But depending upon what else you do inside the controller - if you need things like materializer or actorSystem making them implicit is better.

Related

How to use http client in an Akka actor that has a service?

I have an akka actor, and I would like to use a simple service inside that actor. That service should use the client side api's singleRequest method to fetch something from the local network.
My Actor:
package actor
import actor.WorkerActor._
import akka.actor.Actor
import service.HealthCheckService
import scala.concurrent.ExecutionContext
object WorkerActor {
case object HealthCheck
}
class WorkerActor extends Actor {
implicit val system = context.system
implicit val ec: ExecutionContext = context.system.dispatcher
val healthCheckService = new HealthCheckService()
override def receive: Receive = {
case HealthCheck => sender ! healthCheckService.execute()
}
}
Here I created an ActorSystem and an ExecutionContext as well, so that my service can use it:
package service
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
class HealthCheckService(implicit ec: ExecutionContext, implicit val system: ActorSystem) {
def execute() = {
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://someRandom.url"))
and do something with the response....
}
}
If I don't pass an executionContext into the service I get the error:
[error] Cannot find an implicit ExecutionContext. You might pass
[error] an (implicit ec: ExecutionContext) parameter to your method
[error] or import scala.concurrent.ExecutionContext.Implicits.global.
And if I don't pass an actorsystem into the service I get the error:
[error] could not find implicit value for parameter system: akka.actor.ActorSystem
[error] val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://someRandom.url"))
Questions:
How should services be correctly used from an Actor?
Is it correct to pass around the ActorSystem and ExecutionContext, why doesn't it happen
under the hood?
Http.apply signature is
def apply()(implicit system: ActorSystem): HttpExt
so you need to pass ActorSystem to it implicitly or explicitly.
I do not see anything that requires ExecutionContext inside service, so suppose you do something with Future you get from singleRequest call. Any method on Future will require execution context.
So code is correct, but could be simplified a little:
class WorkerActor extends Actor {
// you do not need to declare implicit val if you just need to pass it once
val healthCheckService = new HealthCheckService(context.system)
override def receive: Receive = {
case HealthCheck => sender ! healthCheckService.execute()
}
}
class HealthCheckService(implicit val system: ActorSystem) {
// dispatcher is already implicit so you need just import it, not declare another val
import system.dispatcher
def execute() = {
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://someRandom.url"))
and do something with the response....
}
}
Also, from your code for service I have an impression that you manipulate somehow with future, probably adding await (as soon as your service per your question does not really need ActorSystem), instead using akka and piping messages to actors from Future. Probably (as answer on you first question), you need to check later example of using singleRequest with actors from link to akka-http you added in question .

scala play 2.5 and dependency injection into actor

I am a little confused with the play documentation https://www.playframework.com/documentation/2.5.x/ScalaAkka
Looking at the examples, one can start an actor from a controller:
import play.api.mvc._
import akka.actor._
import javax.inject._
#Singleton
class Application #Inject() (system: ActorSystem) extends Controller {
val actor = system.actorOf(Props(classOf[AnActor], "anActor")
//...
}
Or one can rely on Guice to instantiate that actor
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
import actors.ConfiguredActor
class MyModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[AnActor]("anActor")
}
}
When the actor is instantiated by Guice, it is possible to inject a dependency into it
import akka.actor._
import javax.inject._
import play.api.Configuration
class AnActor #Inject() (configuration: Configuration) extends Actor {
//...
}
However, starting that actor from a controller raises an
[IllegalArgumentException: no matching constructor found on class AnActor for arguments []]
Is there a way to inject a service into a non-Guice-instantiated actor?
You could use bindActorFactory and then use a factory for creating actors in controllers.
Bind an actor factory. This is useful for when you want to have child actors injected, and want to pass parameters into them, as well as have Guice provide some of the parameters.
You nead a Factory in your Actor's companion object.
import akka.actor._
object AnActor {
trait Factory {
def apply(): Actor
}
}
class AnActor #Inject() (configuration: Configuration) extends Actor {
def receive = ???
}
You use bindActorFactory in your Module
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
import actors._
class MyModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActorFactory[AnActor, AnActor.Factory]
}
}
Then, you could instantiate your actors from your controller using the actor system (so they will be childs of /user actor) and guice will do its magic.
class LocationsController #Inject()(actorSystem: ActorSystem){
def injectedChild2(create: => Actor, name: String, props: Props => Props = identity)(implicit system: ActorSystem): ActorRef = {
system.actorOf(props(Props(create)), name)
}
val actor1: ActorRef = injectedChild2(childFactory(),"anActor1")(actorSystem)
val actor2: ActorRef = injectedChild2(childFactory(),"anActor2")(actorSystem)
}
You could add parameters to the Factory.apply if you like to pass parameters that aren't injected by guice, in this case you need to annotate this parameters with #Assisted
object AnActor {
trait Factory {
def apply(someValue: String): Actor
}
}
class AnActor #Inject() (configuration: Configuration,#Assisted someValue: String) extends Actor {
def receive = ???
}
class LocationsController #Inject()(actorSystem: ActorSystem){
def injectedChild2(create: => Actor, name: String, props: Props => Props = identity)(implicit system: ActorSystem): ActorRef = {
system.actorOf(props(Props(create)), name)
}
val actor1: ActorRef = injectedChild2(childFactory("value fom actor 1"),"anActor1")(actorSystem)
val actor2: ActorRef = injectedChild2(childFactory("value from actor 2"),"anActor2")(actorSystem)
}
The example and the method injectedChild2 are taken from the original Play example but modified to create actors from controllers.
UPDATE:
check this answer to know why you should avoid create actors from a controller, in the words of James Roper.

injecting service into actor gives NullPointerException

I'm not able to figure out how to inject a Service into an Actor.
I tried out several approaches and think this one should fit best.
I think the major problem is that I provide the websocket to the succeeding actor as an parameter. When I pull the Inject up to the class signature compilation doesn't perform because of signature conflict.
I tried adding a currying implicit class declaration to inject the service, but this didn't work out either.
What shall I do to get it right?
Running this code results in a NullPointerException.
package actors
import akka.actor._
import com.google.inject.Inject
import play.api.libs.json._
import models._
import services.WeatherService
/**
* Created by jbc on 28.12.16.
*/
class WeatherWSActor (out: ActorRef) extends Actor {
#Inject() val weatherService: WeatherService = null
def handleMessage(msg: JsValue): JsValue = {
val req = msg.as[Request]
req match {
case Request("daily") => Json.toJson(weatherService.getFourDays())
case Request("next") => Json.toJson(weatherService.getNextHour())
}
}
override def receive: Receive = {
case msg: JsValue => out ! handleMessage(msg)
case _ => out ! "Bad Request"
}
#scala.throws[Exception](classOf[Exception])
override def postStop(): Unit = {
}
}
object WeatherWSActor {
def props(out: ActorRef) = Props(new WeatherWSActor(out))
}
Controller Code:
class WeatherWSController #Inject() (implicit system: ActorSystem, materializer: Materializer) extends Controller {
def socket = WebSocket.accept[JsValue, JsValue] { request =>
ActorFlow.actorRef(out => WeatherWSActor.props(out));
}
}
The service is set up with this Module Code
class Module extends AbstractModule {
override def configure(): Unit = {
bind(classOf[WeatherService]).to(classOf[WeatherServiceImpl]).asEagerSingleton()
}
}
This is covered in the documentation, but here's a summary.
If you want to inject your actors, you need to change a couple of things. First off, the injection point is the constructor, so
class WeatherWSActor (out: ActorRef) extends Actor {
#Inject() val weatherService: WeatherService = null
// ...
}
should become
class WeatherWSActor #Inject() (out: ActorRef,
weatherService: WeatherService) extends Actor {
// ...
}
In fact, assigning null to a Scala val is also going to give you issues.
Secondly, declare a binding for the actor instead of creating it yourself.
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport
import actors.WeatherWSActor
class MyModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[WeatherWSActor]("weather-actor")
}
}
On a side note, you'll also need to create a named binding for out and change the definition of WeatherWSActor to reflect this:
class WeatherWSActor #Inject() (#javax.inject.Named("out-actor") out: ActorRef,
weatherService: WeatherService) extends Actor {
// ...
}
out-actor is whichever class you have set up in the module, e.g.
class MyModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[WeatherWSActor]("weather-actor")
bindActor[SomeOutActor]("out-actor")
}
}
But...
Since you're getting out when a web socket is created, you're (presumably) going to have multiple WeatherWSActor instances. This is going to effect how you handle the dependency injection, so it make more sense to not inject outActor (and hence not create a binding for it), but rather have a singleton WeatherWSActor that broadcasts to all instances of OutActor addressed at a certain point in the actor system.

Scala/Akka/Guice dynamically injecting child actors

I would like to be able to create multiple instances of the same parent actor, but with different child actors. I assume this has to be possible with Guice, but I haven't found the solution.
Here is what I have in mind ~
Controller:
class Application #Inject()(#Named(ParentActor.parentActor1) parentActor1: ActorRef,
#Named(ParentActor.parentActor2) parentActor2: ActorRef)
extends Controller {
def index = Action {
parentActor1 ! "Message"
parentActor2 ! "Message"
Ok()
}
}
Parent Actor:
object ParentActor {
final val parentActor1 = "parentActor1"
final val parentActor2 = "parentActor2"
}
class ParentActor #Inject() (childActor: ActorRef) extends Actor {
def receive = {
case "Message" =>
println(s"ParentActor ${self.path} received message...")
childActor ! "Message"
}
}
Child Actor A:
class ChildActorA extends Actor {
def receive = {
case "Message" =>
println("ChildActorA received message...")
}
}
Child Actor B:
class ChildActorB extends Actor {
def receive = {
case "Message" =>
println("ChildActorB received message...")
}
}
Module:
class Modules extends AbstractModule with AkkaGuiceSupport {
override def configure() = {
bindActor[ParentActor](ParentActor.parentActor1)
bindActor[ParentActor](ParentActor.parentActor2)
}
}
What if I wanted "parentActor1" to have have its "childActor" ref point to an instance of ChildActorA and "parentActor2" to have its "childActor" ref point to an instance of ChildActorB? Is this possible to achieve with Guice?
I'm using some code based on https://github.com/rocketraman/activator-akka-scala-guice to accomplish something similar
I'm not using Play, so I have to initialize Guice and bootstrap the actor system
import akka.actor._
import javax.inject.{Inject, Provider, Singleton}
import com.google.inject.AbstractModule
import net.codingwell.scalaguice.InjectorExtensions._
import com.google.inject.Guice
import com.google.inject.Injector
import scala.concurrent.Await
import scala.concurrent.duration.Duration
object Bootstrap extends App {
val injector = Guice.createInjector(
new AkkaModule(),
new ServiceModule()
)
implicit val system = injector.instance[ActorSystem]
val parentActor1 = system.actorOf(ParentActor.props(ChildActorA.name))
val parentActor2 = system.actorOf(ParentActor.props(ChildActorB.name))
parentActor1 ! "Message"
parentActor2 ! "Message"
system.terminate()
Await.result(system.whenTerminated, Duration.Inf)
}
To initialize Guice there are two classes/objects:
One to initialize the extension and inject the actor system where required
import akka.actor.ActorSystem
import AkkaModule.ActorSystemProvider
import com.google.inject.{AbstractModule, Injector, Provider}
import com.typesafe.config.Config
import net.codingwell.scalaguice.ScalaModule
import javax.inject.Inject
object AkkaModule {
class ActorSystemProvider #Inject() (val injector: Injector) extends Provider[ActorSystem] {
override def get() = {
val system = ActorSystem("actor-system")
GuiceAkkaExtension(system).initialize(injector)
system
}
}
}
class AkkaModule extends AbstractModule with ScalaModule {
override def configure() {
bind[ActorSystem].toProvider[ActorSystemProvider].asEagerSingleton()
}
}
another one to create the providers for the children
import javax.inject.Inject
import akka.actor.{Actor, ActorRef, ActorSystem}
import com.google.inject.name.{Named, Names}
import com.google.inject.{AbstractModule, Provides, Singleton}
import net.codingwell.scalaguice.ScalaModule
class ServiceModule extends AbstractModule with ScalaModule with GuiceAkkaActorRefProvider {
override def configure() {
bind[Actor].annotatedWith(Names.named(ChildActorA.name)).to[ChildActorA]
bind[Actor].annotatedWith(Names.named(ChildActorB.name)).to[ChildActorB]
}
#Provides
#Named(ChildActorA.name)
def provideChildActorARef(#Inject() system: ActorSystem): ActorRef = provideActorRef(system, ChildActorA.name)
#Provides
#Named(ChildActorB.name)
def provideChildActorBRef(#Inject() system: ActorSystem): ActorRef = provideActorRef(system, ChildActorB.name)
}
The extension
import akka.actor._
import com.google.inject.Injector
class GuiceAkkaExtensionImpl extends Extension {
private var injector: Injector = _
def initialize(injector: Injector) {
this.injector = injector
}
def props(actorName: String) = Props(classOf[GuiceActorProducer], injector, actorName)
}
object GuiceAkkaExtension extends ExtensionId[GuiceAkkaExtensionImpl] with ExtensionIdProvider {
override def lookup() = GuiceAkkaExtension
override def createExtension(system: ExtendedActorSystem) = new GuiceAkkaExtensionImpl
override def get(system: ActorSystem): GuiceAkkaExtensionImpl = super.get(system)
}
trait NamedActor {
def name: String
}
trait GuiceAkkaActorRefProvider {
def propsFor(system: ActorSystem, name: String) = GuiceAkkaExtension(system).props(name)
def provideActorRef(system: ActorSystem, name: String): ActorRef = system.actorOf(propsFor(system, name))
}
producer
import akka.actor.{IndirectActorProducer, Actor}
import com.google.inject.name.Names
import com.google.inject.{Key, Injector}
class GuiceActorProducer(val injector: Injector, val actorName: String) extends IndirectActorProducer {
override def actorClass = classOf[Actor]
override def produce() = injector.getBinding(Key.get(classOf[Actor], Names.named(actorName))).getProvider.get()
}
and your actors
import javax.inject.Inject
import akka.actor._
object ParentActor {
def props(childName: String)(implicit #Inject() system: ActorSystem) = Props(classOf[ParentActor],system.actorOf(GuiceAkkaExtension(system).props(childName)))
}
class ParentActor (childActor: ActorRef) extends Actor {
def receive = {
case "Message" =>
println(s"ParentActor ${self.path} received message...")
childActor ! "Message"
}
}
object ChildActorA extends NamedActor{
override final val name = "ChildActorA"
def props() = Props(classOf[ChildActorA])
}
class ChildActorA extends Actor {
def receive = {
case "Message" =>
println("ChildActorA received message...")
}
}
object ChildActorB extends NamedActor{
override final val name = "ChildActorB"
def props() = Props(classOf[ChildActorB])
}
class ChildActorB extends Actor {
def receive = {
case "Message" =>
println("ChildActorB received message...")
}
}
the output from sbt
> run
[info] Running Bootstrap
ParentActor akka://actor-system/user/$b received message...
ParentActor akka://actor-system/user/$d received message...
ChildActorB received message...
ChildActorA received message...
[success] Total time: 1 s, completed Jun 14, 2016 1:23:59 AM
You have to explicitly name the children,
It's not the purest or most elegant answer, and I'm sure the code can be optimized, but it allows you to create instances of the same parent with different children.
I'm thinking that you can also use BindingAnnotations

How to pass an implicit value to an instance thats retrieved by guice

Consider the following class:
class MyClass #Inject() (ws: WSClient)(implicit executionContext: ExecutionContext)
and the code that gets this class:
app.injector.instanceOf[MyClass]
From what i understand the guice injector, injects an ExecutionContext into that implicit ExecutionContext, but in some cases i would like to give that instance a differentExecutionContext
How am i supposed to do that.
Thanks.
You could mark an implicit parameter with annotation #Named and define a binding for the "named" ExecutionContext.
class MyClass #Inject() (ws: WSClient)
(implicit #Named("myEC") executionContext: ExecutionContext)
The binding:
package my.modules
import scala.concurrent.ExecutionContext
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class MyExecutionContextModule extends AbstractModule {
override def configure(): Unit = {
bind(classOf[ExecutionContext]).annotatedWith(Names.named("myEC"))
.to(classOf[MyExecutionContextImpl])
// .toInstance(myExecutionContext)
}
}
Then you need to enable the module in Play configuration
play.modules.enabled += "my.modules.MyExecutionContextModule"
See Guice docs for more information about annotations. You can also define your own annotation or create a Module to bind implementation for your MyClass class (then it is better to make it a trait and implement it in a different class). The only Play specific thing here is that you need to enable module in config if you define one.