Akka and cake pattern
Asked Answered
E

2

12

I'm confused how to ensure that my Actors have the appropriate dependencies using the cake pattern. I'm still getting to grips with this, and I can't find any examples anywhere. I'm basically just looking for a tutorial/resource to follow.

Cheers, Chris.

Eulogy answered 14/4, 2013 at 5:28 Comment(0)
B
10

Actors as dependency:

trait DBComponent {
   def db: ActorRef // no compile time guarantees

   type K
   type V

   object DBActor {
      case class Put(key: K, value: V)
      case class Get(key: K)
   }

   class DBActor {
      import DBActor._
      val db = scala.collection.mutable.Map.empty[K, V]
      def receive = {
         case Put(k, v) => db.put(k, v)
         case Get(k) => sender ! db.get(k)
      }
   }
}

trait ServiceComponent {
   this: DBComponent =>

   import DBActor._

   // you could be talking to deadLetters for all you know
   def put(k: K, v: V): Unit = db ! Put(k, v)
   def get(k: K): Option[V] = {
      implicit val timeout = Timeout(5 seconds)
      val future = ask(actor, Get(k)).mapTo[Option[V]]
      Await.result(future, timeout.duration)
   }
}

Actors having dependencies (where there is nothing special to it):

trait DBComponent {
   def db: DB

   type K
   type V

   trait DB {
      def put(key: K, value: V): Unit
      def get(key: K): Option[V]
   }
}

trait ServiceComponent {
   this: DBComponent =>

   object ServiceActor {
      case class Put(key: K, value: V)
      case class Get(key: K)
   }

   class ServiceActor {
      import ServiceActor._
      def receive = {
         case Put(k, v) => db.put(k, v) // db is in scope
         case Get(k) => sender ! db.get(k)
      }
   }
}
Brim answered 14/4, 2013 at 16:40 Comment(5)
What about the other way around, not things having actors as dependencies, but actors having dependencies, which is what I think the OP was asking about.Queensland
But my issue is that when ServiceActor needs to pass a message to OtherServiceActor then the standard way that I read seems to be using the "context.actorOf[OtherServiceActor]" method to create the required actor. At this point, I get errors if the OtherServiceActor is wrapped in a trait as it unable to instantiate it in that scope.Eulogy
Ok, started typing on hell of an edit and realized the answer to my own questions half way through.Eulogy
Does the Actor class defined in a trait work? As far as I know Akka actors can't be non-static inner classes or it will fail with akka.actor.ActorInitializationException: exception during creationCroup
Ok, just tried it and it does throw that exception if instantiated with system.actorOf(Props[ServiceActor]) but works with system.actorOf(Props(new ServiceActor))Croup
T
2

One additional gotcha, as pointed out by Owen, is that creating actors using actorOf(Props[MyClass]) won't work for inner classes. Ie: the following will fail:

trait MyComponent {
  class MyActor {
    def receive = ...
  }
}

new MyComponent {
  val myActorRef = system.actorOf( Props[MyActor] )
}

According to the documentation at http://doc.akka.io/docs/akka/snapshot/scala/actors.html,

if they are not declared within a top-level object then the enclosing instance’s this reference needs to be passed as the first argument

However, this does not seem to be supported by the scaladoc Props method signatures. The only way around this I found was to use the Props(actor: Actor) constructor, instantiating the actor mysql and passing it to Props.

val myActorRef = system.actorOf( Props( new MyActor(arg1, arg2) ) )

Would be interested to know if there's a better way..

Tasteful answered 22/9, 2013 at 18:15 Comment(1)
You could use actorOf(Props(classOf[SomeActor], someArgument)).Heflin

© 2022 - 2024 — McMap. All rights reserved.