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
Related
How to define a wrapper function/class addActorToSystem() for
trait Stage extends Actor
class Stage1(i:Int) extends Stage
class Stage2(i:Int) extends Stage
and
implicit val system = ActorSystem("mySystem")
instead of the call to
system.actorOf(Props(new Stage1(123)), "myStage1")
The following does not work
You cannot create an instance of [Stage2] explicitly using the constructor (new)
def addActorToSystem(act: Stage, i: Int)(name: String)(implicit sys: ActorSystem) = {
sys.actorOf(Props(act(i)), name)
}
Maybe something like this would help you:
def addActorToSystem[T <: Stage](act: Class[T], i: Int)(name: String)(implicit sys: ActorSystem) = {
sys.actorOf(Props(act, i), name)
}
And usage like follows:
val s1 = addActorToSystem(classOf[Stage1], 1)("s1")
val s2 = addActorToSystem(classOf[Stage2], 2)("s2")
Creating actor without ActorContext defined (by using constructor) is not allowed.
The only one reason why this works in Props is the fact that constructor invocation is handled as by-name parameter thus its evaluation is deffered
I'm doing a few operations on Futures inside an trait.
trait MyTrait {
//Future based operations
}
Instead of using ExecutionContext.Implicits.global for my Future, I want to use one that is defined in my application.conf.
akka {
my-batch-dispatcher {
type = Dispatcher
executor = "fork-join-executor"
fork-join-executor {
parallelism-min = 10
parallelism-factor = 2.0
parallelism-max = 10
}
throughput = 20
}
}
Inside my actor I can do a lookup to get the execution context.
implicit val ec = context.system.dispatchers.lookup("akka.my-batch-dispatcher")
Now sure how to do this inside my trait.
You can add it as abstract implicit value of the trait:
trait MyTrait {
implicit val ec: ExecutionContext
//Future based operations
}
Then the code the implement the trait should make sure to provide the ExecutionContext. If that is an Actor you may do something like:
class MyActor extends Actor with MyTrait {
implicit val ec = context.system.dispatchers.lookup("akka.my-batch-dispatcher")
def receive = {
case "hello" => println("hello back at you")
case _ => println("huh?")
}
}
I didn't test it but I think this should work.
I have an actor that creates children actors of type Child1 in this example. Child1 constructor takes two String's which are extracted from variables that reside in SomeTrait that is mixed into SomeActor isntance.
trait SuperTrait {
lazy val str1: Option[String] = None
lazy val str2: Option[String] = None
}
trait SomeTrait extends SuperTrait {
override lazy val str1: Option[String] = Some("Str1")
override lazy val str2: Option[String] = Some("Str2")
}
class SomeActor extends Actor {
this: SuperTrait =>
var child: Option[ActorRef] = None
override def preStart(): Unit = {
child = for {
st1 <- str1
st2 <- str2
} yield context.actorOf(Child1.props(st1, st2)))
}
}
Creation on SomeActor instance:
context.actorOf(Props[SomeActor with SomeTrait])
With this I am getting strange error:
SomeActor cannot be cast to SomeTrait.
It seems that extracting variables from Option container of SomeTrait throws that exception.
What am I missing here?
And it doesn't only happen with for comprehensions. Also when I try to do str1.getOrElse("") or add a getter to SomeTrait : def getStr1 = str1.getOrElse("")
As #ggovan said above, when using Akka you can't control the construction of an Actor. The Akka library takes care of that (and that's why you have Props to encapsulate the parameters passed to the constructor). The reason for this is, if your actor crashes its supervisor needs to be able to create a new instance of it.
When you use Props[X], Scala is using a a ClassTag to find the runtime class of the Actor. However, the ClassTag doesn't seem to pick up the anonymous class created when you call Props[X with Y], it picks up only the base class (in your case, SomeActor). A way around that is to use the by-name alternative of Props, so you can do this when creating the SomeActor:
val actor = sys.actorOf(Props(new SomeActor with SomeTrait))
That works and it picks up the overridden lazy vals too. Be sure, however, to read the downsides to that at http://doc.akka.io/docs/akka/snapshot/scala/actors.html (section "Dangerous variants").
Another option would be to use the Cake Pattern:
trait SomeTrait {
lazy val str1: Option[String] = None
lazy val str2: Option[String] = None
}
trait ActorTrait extends Actor {
this: SomeTrait =>
override lazy val str1 = Some("Str1")
override lazy val str2 = Some("Str2")
var child: Option[ActorRef] = None
override def preStart(): Unit = {
child = for {
st1 <- str1
st2 <- str2
} yield context.actorOf(Child1.props(st1, st2)))
}
}
class SomeActor extends ActorTrait with SomeTrait
In that case you can use the recommended Props[SomeActor] method to create the Props object.
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
I'd like to reduce the boilerplate needed to bind these akka actors.
At current, my code looks like this:
bind(classOf[ActorRef]).
annotatedWith(Names.named("mines")).
toProvider(new TypeLiteral[ActorProvider[MyActor]]() {}).
asEagerSingleton()
I'd like it too look like:
bindActor[MyActor].withName("mines")
I've tried to subclass AbstractModule to squeeze this concept in to no avail.
Pertinent code:
class ActorProvider[T <: Actor] #Inject() ( val key:Key[T], val injector:Injector, val system: ActorSystem ) extends Provider[ActorRef] {
def get = {
system.actorOf(Props(injector.getInstance(key)))
}
}
Check out https://github.com/codingwell/scala-guice/. Here is a sample based on it. It allows for
bindActor[MyActor].withName("mines")
Keep in mind you are binding a non-generic ActorRef and not Actor itself. This will create a binding for #Named("foo") ActorRef. You should never work with Actor directly.
You cannot get Key in the provider. Provider doesn't take any contextual injections like you tried with the Key or e.g. Injection Point. What you can do is create a different instance of the provider for each actor binding and inject it with ActorSystem afterwards. Alternatively you could change the API to include the actor system instance aswell.
trait AkkaModule extends AbstractModule {
// should be:
// this: AbstractModule =>
// see http://lampsvn.epfl.ch/trac/scala/ticket/3564
import ScalaModule._
private def binderAccess = super.binder // shouldn't need super
def bindActor[T <: Actor](implicit m: Manifest[T]) = new ActorBindingBuilder {
//Hack, no easy way to exclude the bind method that gets added to classes inheriting ScalaModule
//So we experamentally figured out how many calls up is the source, so we use that
//Commit 52c2e92f8f6131e4a9ea473f58be3e32cd172ce6 has better class exclusion
val mybinder = binderAccess.withSource((new Throwable).getStackTrace()(3))
val self = (mybinder bind classOf[ActorRef]).asInstanceOf[AnnotatedBindingBuilder[ActorRef]]
}
}
object AkkaModule {
class ActorProvider(val name: String) extends Provider[ActorRef] {
#Inject var system: ActorSystem = _
def get = {
system.actorFor(system.name + "/user/" + name)
}
}
trait ActorBindingBuilder {
val mybinder: Binder
val self: AnnotatedBindingBuilder[ActorRef]
def withName(name: String) = {
val provider = new ActorProvider(name)
self.annotatedWith(Names.named(name)).toProvider(provider)
mybinder.requestInjection(provider)
}
}
}
Something like this that leverages scala's type Manifest might work
http://www.scala-lang.org/api/current/scala/reflect/Manifest.html
where Foo is analogous to ActorRef and Bla is analogous to MyActor:
scala> import com.google.inject
import com.google.inject
scala> val binder:inject.Binder = null
binder: com.google.inject.Binder = null
scala> class Foo {}
defined class Foo
scala> class Bla extends Foo {}
defined class Bla
scala> def bind[T <: Foo:Manifest] = binder.bind( classOf[Foo] ).toProvider( new
inject.TypeLiteral[inject.Provider[T]](){} ).asEagerSingleton
bind: [T <: Foo](implicit evidence$1: Manifest[T])Unit
Maybe combine that with implicit conversion to convert a Binder to a MyBinder:
http://daily-scala.blogspot.com/2009/08/implicit-methods.html
class MyBinder {
def bindActor[T <: ActorRef:Manifest]( nameToBind:String ):Unit = ...
}
object MyBinder {
implicit def binderToMyBinder( ...
}
Good luck!