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.
Related
I have a code in my Play Scala (2.5x, 2.11.11) app which has been running just fine so far (it is based on the following link: https://fizzylogic.nl/2016/11/27/authorize-access-to-your-play-application-using-action-builders-and-action-functions/). But now I need to pass another class instance to ApplicationAuthorizationHandler class (NOTE: throughout my code I am using Guice DI for injecting parameters into class constructors).
Current code:
class ApplicationAuthorizationHandler
extends AuthorizationHandler {
...
}
trait AuthorizationHandler {
...
}
trait AuthorizationCheck {
def authorizationHandler: AuthorizationHandler = new ApplicationAuthorizationHandler
object AuthenticatedAction extends ActionBuilder[RequestWithPrincipal] {
override def invokeBlock[A](request: Request[A], block: (RequestWithPrincipal[A]) => Future[Result]): Future[Result] = {
def unauthorizedAction = authorizationHandler.unauthorized(RequestWithOptionalPrincipal(None, request))
def authorizedAction(principal: Principal) = block(RequestWithPrincipal(principal, request))
authorizationHandler.principal(request).fold(unauthorizedAction)(authorizedAction)
}
}
}
//Example controller using this trait AuthorizationCheck
class MyController #Inject() extends Controller with AuthorizationCheck {
def myAction = AuthenticatedAction { implicit request =>
...
}
Desired code:
class ApplicationAuthorizationHandler #Inject() (userService: UserService)
extends AuthorizationHandler {
...
// userService is used here
}
But since the instance of ApplicationAuthorizationHandler is instantiated inside trait AuthorizationCheck I can't inject UserService instance into it. I am Mixin this trait with all controllers so would like to keep the same way unless there is a better way (and there must be).
First, is there a way to inject directly into class/trait method ?
Alternatively, is there a way where I don't instantiate ApplicationAuthorizationHandler in trait AuthorizationCheck and pass it during run-time inside the controller ?
Or any other way ?
A trait does not need to provide an implementation, so you can have something like:
trait AuthorizationHandler {
...
}
class ApplicationAuthorizationHandler extends AuthorizationHandler {
...
}
trait AuthorizationCheck {
// just declaring that implementations needs to provide a
def authorizationHandler: AuthorizationHandler
object AuthenticatedAction extends ActionBuilder[RequestWithPrincipal] {
override def invokeBlock[A](request: Request[A], block: (RequestWithPrincipal[A]) => Future[Result]): Future[Result] = {
def unauthorizedAction = authorizationHandler.unauthorized(RequestWithOptionalPrincipal(None, request))
def authorizedAction(principal: Principal) = block(RequestWithPrincipal(principal, request))
authorizationHandler.principal(request).fold(unauthorizedAction)(authorizedAction)
}
}
}
// So, now this controller needs to provide a concrete implementation
// of "authorizationHandler" as declared by "AuthorizationCheck".
// You can do it by injecting a "AuthorizationHandler" as a val with
// name authorizationHandler.
class MyController #Inject()(val authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {
def myAction = AuthenticatedAction { implicit request =>
...
}
}
And of course, you need to provide a module to bind AuthorizationHandler to ApplicationAuthorizationHandler:
import play.api.inject._
class AuthorizationHandlerModule extends SimpleModule(
bind[AuthorizationHandler].to[ApplicationAuthorizationHandler]
)
Of course, ApplicationAuthorizationHandler can have its own dependencies injected. You can see more details at our docs.
There are many cases when you cannot use the #Inject approach of guice. This is true when dependencies are needed inside of trait and also actors.
The approach I use in these cases is that I put my injector in a object
object Injector {
val injector = Guice.createInjector(new ProjectModule())
}
since the above is inside of an object, you can access it from anywhere. (its like a singleton).
Now inside your trait or an actor when you need the user service do
trait Foo {
lazy val userService = Injector.injector.getInstance(classOf[UserService])
}
Don't forget to make the variable lazy, because you want the instance to be created as late as possible when the injector has already been created.
I have a WebSocket controller which creates per connection actor handler:
class WebSocketController #Inject()(cc: ControllerComponents)(implicit exc: ExecutionContext) {
def socket: WebSocket = WebSocket.accept[JsValue, JsValue] { request =>
ActorFlow.actorRef { out => // Flow that is handled by an actor from 'out' ref
WebSocketActor.props(out) // Create an actor for new connected WebSocket
}
}
}
And inside the actor handler I need to work with ReactiveMongo:
trait ModelDAO extends MongoController with ReactiveMongoComponents {
val collectionName: String
...
}
class UsersCollection #Inject()(val cc: ControllerComponents,
val reactiveMongoApi: ReactiveMongoApi,
val executionContext: ExecutionContext,
val materializer: Materializer)
extends AbstractController(cc) with ModelDAO {
val collectionName: String = "users"
}
So, the usual way is to #Inject() UsersCollection in the target class. But I can't do something like:
class WebSocketActor #Inject()(out: ActorRef, users: UsersCollection) extends Actor { ... }
Because instances of actor creates inside WebSocketActor companion object:
object WebSocketActor {
def props(out: ActorRef) = Props(new WebSocketActor(out))
}
How do I use UsersCollection inside the WebSocketActor?
You can create actor who's dependencies will be injected automatically by Play. No problem. (https://www.playframework.com/documentation/2.6.x/ScalaAkka)
But in case of web sockets, Props of the actor is expected, but not Actor (or ActorRef) itself.
ActorFlow.actorRef { out => // Flow that is handled by an actor from 'out' ref
WebSocketActor.props(out) // <- ACTOR IS NOT CREATED HERE, WE RETURN PROPS
}
So there is no way to do it automatically in this case (At least I didn't find it).
What you can do is to pass UsersCollection manually.
class WebSocketController #Inject()(cc: ControllerComponents, usersCollection: UsersCollection)(implicit exc: ExecutionContext) {
def socket: WebSocket = WebSocket.accept[JsValue, JsValue] { request =>
ActorFlow.actorRef { out => // Flow that is handled by an actor from 'out' ref
WebSocketActor.props(out, usersCollection) //<- ACTOR IS NOT CREATED HERE, WE RETURN PROPS
}
}
}
Noticed that I injected UsersCollection into WebSocketController and pass it to props.
Simple and I do not see any disadvantage.
I have an event handler module inside my play app that basically instantiates all actors upon application startup. event handlers are all actors so you could do it like this:
class EventHandlerBootstrap #Inject() (system: ActorSystem, app: Application) {
EventHandlerBootstrap.handlers.foreach {
case (h, n) => system.actorOf(Props(app.injector.instanceOf(h)), n)
}
}
//These Class[_ <: EventHandler] are classes of user defined actors each with their own
// dependencies which guice will take care of automattically.
object EventHandlerBootstrap {
val handlers: Map[Class[_ <: EventHandler], String] = Map(
classOf[UserRegisteredHandler] -> "user-registered-handler",
classOf[CustomerOrderCreatedHandler] -> "customer-order-created-handler",
classOf[DisputeClosedHandler] -> "dispute-closed-handler",
classOf[Throttle] -> "throttle"
)
}
and inside the module i run the bootstrapper like this:
class EventModule extends ScalaModule with AkkaGuiceSupport {
override def configure(): Unit = {
bind[EventHandlerBootstrap].asEagerSingleton()
}
}
if you're ofcourse not eager to blindly follow my recipe you can still take out the fact that injecting your actors and their dependencies is fully supported by guice as shown above. Hope It'll help
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.
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.
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