I have an actor
import akka.actor.{Props, Actor}
import scala.reflect.ClassTag
class MyActor1[T<: Actor: ClassTag] extends Actor {
//....
}
import akka.actor.{Props, Actor}
import scala.reflect.ClassTag
class MyActor2[T <: Actor: ClassTag] extends Actor {
def receive = {
case Start =>
val actor1 = context actorOf Props[MyActor3] //MyActor3 is another actor
actor1 ! Get
case Result(ids: List[Int]) =>
val myActor1List = ids map { new MyActor1[T](_) }
myActor1List foreach { _ ! SomeMessage } // error: "!" is not a member of MyActor1[T]
}
}
The error is "!" is not a member of MyActor1[T]
That is because ! is a member of ActorRef, not Actor. You should always access actors via ActorRefs, not via instances of your classes that extend Actor directly.
Your myActor1List contains instances of class MyActor1, instead of ActorRefs. Create your actors by calling context.actorOf[MyActor1] instead of instantiating MyActor1 objects directly.
You're already doing it in your own code. Notice the difference between:
// Create an actor and get an ActorRef
val actor1 = context actorOf Props[MyActor3]
// Create instances of your actor class directly
val myActor1List = ids map { new MyActor1[T](_) }
Related
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'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.
Is it possible to create an akka actor that has implicits in the constructor?
Having:
class Actor(parameter: Long)(implicit service:Service)
and
class Service
can I use the context to create the actor like this?
implicit val service:Service = new Service()
val someLong = 3
context.actorOf(FromConfig.props(Props(classOf[Actor], someLong)), "actor")
As a mention the service cannot be passed to the constructor because multiple different actor classes can be received, which use different implicits from the scope.
Define your Props in Companion object of Actor and is good for injecting Dependencies.
class SomeActor(parameter: Long)(implicit service:Service) extends Actor {
def receive = {
case message => // Do your stuff
}
}
object SomeActor {
def props(parameter: Long)(implicit service:Service) = Props(new SomeActor(parameter))
}
implicit val service:Service = new Service()
val someLong = 3
val ref = context.actorOf(SomeActor.props(someLong)), "actor")
and you can read more about Dependency Injection here :
http://doc.akka.io/docs/akka/snapshot/scala/actors.html#Dependency_Injection
http://letitcrash.com/post/55958814293/akka-dependency-injection
I am fairly new to akka and not sure about approaching this problem.
I have a Monitor actor that spawns 2 other actors as DiskMonitor and MemoryMonitor
DiskMonitor checks the diskUsage and report DiskReport message
MemoryMonitor checks the diskUsage and report MemoryReport message
I want to combine the DiskReport and MemoryReport into Report Object and send to to remote machine via an API.
Questions
Since DiskMonitor and MemoryMonitor both are actors they send the response back to caller once they are done with their work
Monitor
diskMonitor ! DiskMonitorRequest
memoryMonitor ! MemoryMonitorRequest
How would Monitor know that it has all the results that it needs so that it can create Report object and send it via API?
Are actors good way to approach this problem?
I also read about future but I don't understand them well and not sure if they would be helpful in this problem context
This is a simple way of doing these things. Other options can be through using context.become or the trait FSM.
class Monitor extends Actor {
// Somewhere do this:
diskMonitor ! DiskMonitorRequest
memoryMonitor ! MemoryMonitorRequest
var diskMonitorResult = Option.empty[DiskMonitor]
var memoryMonitorResult = Option.empty[MemoryMonitor]
def recieve = {
case d: DiskMonitor =>
diskMonitorResult = Some(d)
checkIfCompleted()
case m: MemoryMonitor =>
memoryMonitorResult = Some(m)
checkIfCompleted()
}
def checkIfCompleted = {
(diskMonitorResult, memoryMonitorResult) match {
case (Some(diskMonitor), Some(memoryMonitor)) =>
// send to external API
externalApi ! Report(diskMonitor, memoryMonitor)
// Possibly stop this actor
case _ => // do nothing
}
}
}
You can use ask pattern and combine futures..
import akka.actor._
import akka.pattern.ask
import akka.util.Timeout
import scala.concurrent.ExecutionContextExecutor
import scala.concurrent.duration._
import scala.language.postfixOps
class Monitor extends Actor with ActorLogging {
implicit val timeout: Timeout = Timeout(5 seconds)
implicit val ec: ExecutionContextExecutor = context.dispatcher
val diskM: ActorRef = context.system.actorOf(Props[DiskMonitor])
val memoryM: ActorRef = context.system.actorOf(Props[MemoryMonitor])
val remoteM: ActorRef = context.system.actorOf(Props[RemoteMachine])
override def preStart: Unit = {
log.info("Monitor Created..")
self ! GenerateReport
}
override def receive: Receive = {
case GenerateReport =>
(diskM ? MakeDiskReport).mapTo[DiskReport].zip((memoryM ? MakeMemoryReport)
.foreach { case (diskR, memR) =>
remoteM ! Report(memR.report, diskR.report)
}
}
}
class DiskMonitor extends Actor with ActorLogging {
override def receive: Receive = {
case MakeDiskReport =>
log.info("Creating DiskReport..")
val client = sender
client ! DiskReport("DiskReport")
}
}
class MemoryMonitor extends Actor with ActorLogging {
override def receive: Receive = {
case MakeMemoryReport =>
log.info("Creating MemoryReport..")
val client = sender
client ! MemoryReport("MemoryReport")
}
}
class RemoteMachine extends Actor with ActorLogging {
override def receive: Receive = {
case Report(memr, diskr) =>
log.info(s"Final Report.. $memr, $diskr")
}
}
object Main extends App {
val sys = ActorSystem()
sys.actorOf(Props[Monitor])
}
sealed trait Message
case object GenerateReport extends Message
case object MakeDiskReport extends Message
case object MakeMemoryReport extends Message
case class DiskReport(report: String) extends Message
case class MemoryReport(report: String) extends Message
case class Report(memReport: String, diskReport: String) extends Message
Below class is causing an error at line new HelloWorld :
Exception in thread "main" akka.actor.ActorInitializationException: You cannot create an instance of [HelloWorld] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.
at akka.actor.ActorInitializationException$.apply(Actor.scala:219)
at akka.actor.Actor$class.$init$(Actor.scala:436)
at HelloWorld.<init>(HelloWorld.scala:4)
at Driver$.main(HelloWorld.scala:38)
at Driver.main(HelloWorld.scala)
So I try : val hw = actorOf(new HelloWorld)
But this causes a compiler error :
not found: value actorOf
How should HelloWorld below be implemented ?
Reading other Scala docs an act method is requried to be defined within the class that extends Actor and then invoke the start method on this class, is there a reason for using actorOf instead of defining an act method ?
Below class is taken from Scala akka docs http://doc.akka.io/docs/akka/2.2.0/scala.html :
import akka.actor.Actor
import akka.actor.Actor._
import akka.actor.Props
class HelloWorld extends Actor {
override def preStart(): Unit = {
// create the greeter actor
val greeter = context.actorOf(Props[Greeter], "greeter")
// tell it to perform the greeting
greeter ! Greeter.Greet
}
def receive = {
// when the greeter is done, stop this actor and with it the application
case Greeter.Done => context.stop(self)
}
object Greeter {
case object Greet
case object Done
}
class Greeter extends Actor {
def receive = {
case Greeter.Greet =>
println("Hello World!")
sender ! Greeter.Done
}
}
}
object Driver {
def main(args: Array[String]) {
new HelloWorld
}
}
You need to edit your main as shown below. Secondly, in line-5, you need to change it to context.actorOf(Props(new Greeter)). This is because your Greeter does not have apply function defined, hence you need to manually create Greeter object yourself.
Working code below:
import akka.actor.ActorSystem
class HelloWorld extends Actor {
override def preStart(): Unit = {
// create the greeter actor
val greeter = context.actorOf(Props(new Greeter), "greeter")//line 5
// tell it to perform the greeting
greeter ! Greeter.Greet
}
def receive = {
// when the greeter is done, stop this actor and with it the application
case Greeter.Done => context.stop(self)
}
object Greeter {
case object Greet
case object Done
}
class Greeter extends Actor {
def receive = {
case Greeter.Greet =>
println("Hello World!")
sender ! Greeter.Done
}
}
}
object Driver {
def main(args: Array[String]) {
val system = ActorSystem("Main")
val ac = system.actorOf(Props[HelloWorld])
}
}
If you want to use your main class do the following:
import akka.actor.{ActorSystem, Props}
object Driver extends App {
val system = ActorSystem("System")
val hw = system.actorOf(Props[HelloWorld], name = "hw")
}
Which will create a new actor system and then create the HelloWorld actor using that actor system.
You can also follow the akka instructions:
set Akka.Main as the main class and give the program "com.example.HelloWorld" as argument.
val greeter = context.actorOf(Props(new Greeter), "greeter")//line 5
I don't think you need to have a new keyword there for Greeter. I believe the Props does that for you already. If anything having a new should be a