Question

Recently I was switching from scala actors to akka actors, but noticed that akka actors use ActorRef instead of the instance object:

val actorRef: ActorRef = Actor.actorOf(new MyActor)

So I tried:

val myActor = new MyActor
val actorRef: ActorRef = Actor.actorOf(x)

... to have both: 1) ActorRef to send messages and 2) MyActor to call methods on.
But I got:

akka.actor.ActorInitializationException: ActorRef for instance of actor [MyActor] is not in scope.

So my question is: How can I obtain an instance (of some type) on which I can call ActorRef-like methods like ! AND also methods from the MyActor instance?

Was it helpful?

Solution

What you're doing is a terrible idea. So just stop right now, step away from the keyboard, and go to the Akka Documentation and read up on Actors.

Consider this:

class YourActor extends Actor {
  var mutableStuff = ...
  def receive = {
    case _ =>
      // mess with mutableStuff
  }
  def publicMethod = // mess with mutableStuff
}

Now, set up your system and start sending messages and calling that method from other threads. Boom!

You're doing precisely what Akka and the Actor model help you prevent. You're actually bending over backwards to break what they've already fixed :) They won't let you do it.

Now, you can unit test by accessing methods directly but you need a TestActorRef for that. While you're reading the docs, read through the section on Testing.

OTHER TIPS

The best that I can up with is the following, quite dirty:
Is there a better way?

import akka.actor._

trait ActorCom {
  var actorRefForInitialization: ActorRef = _
  lazy val actorRef: ActorRef = actorRefForInitialization
  def ?(message: Any)(implicit channel: UntypedChannel = NullChannel, timeout: Actor.Timeout = Actor.defaultTimeout) = actorRef ? message
  def !(msg: Any)(implicit sender: UntypedChannel) = actorRef ! msg
  def start = actorRef.start
}

object AkkaActorFactory {
  def apply[A <: Actor](newInstance: => A with ActorCom): A with ActorCom = {
    var instance: Option[A with ActorCom] = None
    val actorRef = Actor.actorOf({
      instance = Some(newInstance)
      instance.get
    })
    instance.get.actorRefForInitialization = actorRef
    instance.get.actorRef // touch lazy val in ActorCom, to make it equal to actorRef and then its fixed (immutable)
    instance.get
  }
}

class MyActor extends Actor {
  def receive = {
    case "test1" => println("good")
    case "test2" => println("fine")
    case _       => println("bad")
  }
  def sendTestMsg2Myself = self ! "test2"
}

val myActor = AkkaActorFactory(newInstance = new MyActor with ActorCom)
myActor.start
myActor ! "test1"
myActor.sendTestMsg2Myself // example for calling methods on MyActor-instance
myActor ! PoisonPill
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top