Scala akka typed: how to get ActorRef to Actor from it's instance and send message itself? - scala

I want to send message from Actor instance (case class/class from what it's behaviour created) to it's Actor.
I gain it by saving instance, and then save ActorRef in it:
val (instance, behaviour) = MyActorInstance(Nothing)
val actor = ActorSystem(instance, "SomeName123")
//save it here
instance.setMyActor(actor)
object MyActorInstance {
def apply(ctx: ActorContext[Commands]): (MyActorInstance,Behavior[Commands]) = {
val actorInstance = new MyActorInstance(ctx)
val behaviour: Behavior[Commands] =
Behaviors.setup { context =>
{
Behaviors.receiveMessage { msg =>
actorInstance.onMessage(msg)
}
}
}
(actorInstance,behaviour)
}
}
class MyActorInstance(context: ActorContext[Commands]) extends AbstractBehavior[Commands](context) {
protected var myActorRef: ActorRef[Commands] = null
def setMyActor(actorRef: ActorRef[Commands]): Unit = {
myActorRef = actorRef
}
override def onMessage(msg: Commands): Behavior[Commands] = {
msg match {
case SendMyself(msg) =>
myActorRef ! IAmDone(msg)
Behaviors.same
case IAmDone(msg) =>
println(s"Send $msg to myself!")
Behaviors.same
}
}
}
Here i save ActorRef to Actor for it's instance in var myActorRef.
Then i use that myActorRef to send message from Actor's instance to itself by SendMyself message.
But for that, as you see, i am using variables, which is not good: to save ActorRef it's need to rewrite field myActorRef of instance of MyActorInstance class from null to ActorRef - it is possible only with variables.
If I try to use val and create immutable class by rewriting its instance for new, and then swap it from old to new, my Actor actor still linked to old instance where myActorRef == null.
Now I found one way: just using var instead of val or immutable class.
But I want to use val or nothing.
For that I need to get ActorRef from it's instance, but how?

There is no reason for such complex dance, just use ctx.self. And please read at least the most basic documentation before asking, it would even have saved you time.

Related

Mutable collections in akka Actor return NullPointerException

I have an actor which is calling a websocket and updating a map everytime it receives an update form the web socket. the same map is being used by the actor at another point in the command.
class MyActor(broker: InMemoryBroker) extends Actor {
val myMap: TrieMap[String, String] = new TrieMap[String, String]()
//Gets a response every 1 second
webSocket.get(onReponse= r=> myMap(r.key) = r.value)
def receive()={
case MyCommand(key)=>
if(myMap.get(key).isDefined){ //Null pointer exception is thrown here
//Do stuff
}
}
I was expecting TrieMap to be thread safe and not have such issues. Any suggestions?
You should do as much processing as possible in the receive method. So rather than updating the map directly in onResponse, send yourself a message and update the map when you receive it:
class MyActor(broker: InMemoryBroker) extends Actor {
val myMap: TrieMap[String, String] = new TrieMap[String, String]()
private case class UpdateMap(r: ???)
//Gets a response every 1 second
webSocket.get(onReponse = r => self ! UpdateMap(r))
def receive() = {
case UpdateMap(r) =>
myMap(r.key) = r.value
case MyCommand(key) =>
if (myMap.get(key).isDefined) { //Null pointer exception is thrown here
//Do stuff
}
}
}
With the method the TrieMap is only accessed from a single thread at a time.
Also, that test is better done as:
myMap.get(key).foreach{ value =>
???
}

how to use synchronization in scala

I am creating an actor and I want to create it the first time and then use its actor reference whenever that actor is needed. Here is the code
val actorPath = "akka://testActorSystem/user/'"
object ActorManager {
def getTestActorRef: ActorRef = {
var actorRef: Option[ActorRef] = None
this.synchronized {
val sel = system.actorSelection(actorPath + "testActor");
val future1 = sel.resolveOne()
val res: Try[ActorRef] = Await.ready(future1, timeout.duration).value.get
res match {
case Success(actorref) =>
actorRef = Some(actorref)
actorRef.get
case Failure(e) =>
val testActor = system.actorOf(Props[TestActor], name = "testActor")
actorRef = Some(testActor)
}
getActorRef(actorRef.get)
}
actorRef.get
}
}
Q1->Is that the right way to achieve my desired functionality?
The problem I am having is whenever this code is called in two places at the same time.
ActorManager.getTestActorRef
it throws two different exceptions:
akka.actor.ActorNotFound: Actor not found for: ActorSelection[Anchor(akka://testActorSystem/), Path(/user/'testActor)]
at akka.actor.ActorSelection$$anonfun$resolveOne$1.apply(ActorSelection.scala:65) ~[akka-actor_2.11-2.3.6.jar:na]
at akka.actor.ActorSelection$$anonfun$resolveOne$1.apply(ActorSelection.scala:63) ~[akka-actor_2.11-2.3.6.jar:na]
and:
akka.actor.InvalidActorNameException: actor name [testActor] is not unique!
at akka.actor.dungeon.ChildrenContainer$NormalChildrenContainer.reserve(ChildrenContainer.scala:130) ~[akka-actor_2.11-2.3.6.jar:na]
at akka.actor.dungeon.Children$class.reserveChild(Children.scala:77) ~[akka-actor_2.11-2.3.6.jar:na]
at akka.actor.ActorCell.reserveChild(ActorCell.scala:369) ~[akka-actor_2.11-2.3.6.jar:na]
I tried to use this.synchronized but it did not help, the problem arises when I am invoking this method more than once
ActorManager.getTestActorRef
How can I create actor once and use its actorRef again and again without creating it again?
To achieve your goal you just need:
object ActorManager {
val getTestActorRef: ActorRef = system.actorOf(Props[TestActor], name = "testActor")
}
this will create actor once and it will be available. You do not need all these thing with actorSelection, synchronized and so on.
So you will get static reference to ActorRef you can use to send messages. Actor for this reference will be created once in background.

Akka-Streams ActorPublisher does not receive any Request messages

I am trying to continuously read the wikipedia IRC channel using this lib: https://github.com/implydata/wikiticker
I created a custom Akka Publisher, which will be used in my system as a Source.
Here are some of my classes:
class IrcPublisher() extends ActorPublisher[String] {
import scala.collection._
var queue: mutable.Queue[String] = mutable.Queue()
override def receive: Actor.Receive = {
case Publish(s) =>
println(s"->MSG, isActive = $isActive, totalDemand = $totalDemand")
queue.enqueue(s)
publishIfNeeded()
case Request(cnt) =>
println("Request: " + cnt)
publishIfNeeded()
case Cancel =>
println("Cancel")
context.stop(self)
case _ =>
println("Hm...")
}
def publishIfNeeded(): Unit = {
while (queue.nonEmpty && isActive && totalDemand > 0) {
println("onNext")
onNext(queue.dequeue())
}
}
}
object IrcPublisher {
case class Publish(data: String)
}
I am creating all this objects like so:
def createSource(wikipedias: Seq[String]) {
val dataPublisherRef = system.actorOf(Props[IrcPublisher])
val dataPublisher = ActorPublisher[String](dataPublisherRef)
val listener = new MessageListener {
override def process(message: Message) = {
dataPublisherRef ! Publish(Jackson.generate(message.toMap))
}
}
val ticker = new IrcTicker(
"irc.wikimedia.org",
"imply",
wikipedias map (x => s"#$x.wikipedia"),
Seq(listener)
)
ticker.start() // if I comment this...
Thread.currentThread().join() //... and this I get Request(...)
Source.fromPublisher(dataPublisher)
}
So the problem I am facing is this Source object. Although this implementation works well with other sources (for example from local file), the ActorPublisher don't receive Request() messages.
If I comment the two marked lines I can see, that my actor has received the Request(count) message from my flow. Otherwise all messages will be pushed into the queue, but not in my flow (so I can see the MSG messages printed).
I think it's something with multithreading/synchronization here.
I am not familiar enough with wikiticker to solve your problem as given. One question I would have is: why is it necessary to join to the current thread?
However, I think you have overcomplicated the usage of Source. It would be easier for you to work with the stream as a whole rather than create a custom ActorPublisher.
You can use Source.actorRef to materialize a stream into an ActorRef and work with that ActorRef. This allows you to utilize akka code to do the enqueing/dequeing onto the buffer while you can focus on the "business logic".
Say, for example, your entire stream is only to filter lines above a certain length and print them to the console. This could be accomplished with:
def dispatchIRCMessages(actorRef : ActorRef) = {
val ticker =
new IrcTicker("irc.wikimedia.org",
"imply",
wikipedias map (x => s"#$x.wikipedia"),
Seq(new MessageListener {
override def process(message: Message) =
actorRef ! Publish(Jackson.generate(message.toMap))
}))
ticker.start()
Thread.currentThread().join()
}
//these variables control the buffer behavior
val bufferSize = 1024
val overFlowStrategy = akka.stream.OverflowStrategy.dropHead
val minMessageSize = 32
//no need for a custom Publisher/Queue
val streamRef =
Source.actorRef[String](bufferSize, overFlowStrategy)
.via(Flow[String].filter(_.size > minMessageSize))
.to(Sink.foreach[String](println))
.run()
dispatchIRCMessages(streamRef)
The dispatchIRCMessages has the added benefit that it will work with any ActorRef so you aren't required to only work with streams/publishers.
Hopefully this solves your underlying problem...
I think the main problem is Thread.currentThread().join(). This line will 'hang' current thread because this thread is waiting for himself to die. Please read https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join-long- .

Get or create child actor by ID

I have two actors in my system. Talker and Conversation. Conversation consists in two talkers (by now). When a Talker wants to join a conversation I should check if conversation exists (another talker has created it) and if it not, create it. I have this code in a method of my Talker actor:
def getOrCreateConversation(conversationId: UUID): ActorRef = {
// #TODO try to get conversation actor by conversationId
context.actorSelection("user/conversation/" + conversationId.toString)
// #TODO if it not exists... create it
context.actorOf(Conversation.props(conversationId), conversationId.toString)
}
As you can see, when I create my converastion actor with actorOf I'm passing as a second argument the conversationId. I do this for easy searching this actor... Is it the correct way to do this?
Thank you
edited
Thanks to #Arne I've finally did this:
class ConversationRouter extends Actor with ActorLogging {
def receive = {
case ConversationEnv(conversationId, msg) =>
val conversation = findConversation(conversationId) match {
case None => createNewConversation(conversationId)
case Some(x) => x
}
conversation forward msg
}
def findConversation(conversationId: UUID): Option[ActorRef] = context.child(conversationId.toString)
def createNewConversation(conversationId: UUID): ActorRef = {
context.actorOf(Conversation.props(conversationId), conversationId.toString)
}
}
And the test:
class ConversationRouterSpec extends ChatUnitTestCase("ConversationRouterSpec") {
trait ConversationRouterSpecHelper {
val conversationId = UUID.randomUUID()
var newConversationCreated = false
def conversationRouterWithConversation(existingConversation: Option[ActorRef]) = {
val conversationRouterRef = TestActorRef(new ConversationRouter {
override def findConversation(conversationId: UUID) = existingConversation
override def createNewConversation(conversationId: UUID) = {
newConversationCreated = true
TestProbe().ref
}
})
conversationRouterRef
}
}
"ConversationRouter" should {
"create a new conversation when a talker join it" in new ConversationRouterSpecHelper {
val nonExistingConversationOption = None
val conversationRouterRef = conversationRouterWithConversation(nonExistingConversationOption)
conversationRouterRef ! ConversationEnv(conversationId, Join(conversationId))
newConversationCreated should be(right = true)
}
"not create a new conversation if it already exists" in new ConversationRouterSpecHelper {
val existingConversation = Option(TestProbe().ref)
val conversationRouterRef = conversationRouterWithConversation(existingConversation)
conversationRouterRef ! ConversationEnv(conversationId, Join(conversationId))
newConversationCreated should be(right = false)
}
}
}
Determining the existence of an actor cannot be done synchronously. So you have a couple of choices. The first two are more conceptual in nature to illustrate doing asynchronous lookups, but I offer them more for reference about the asynchronous nature of actors. The third is likely the correct way of doing things:
1. Make the function return a Future[ActorRef]
def getOrCreateConversation(conversationId: UUID): Unit {
context.actorSelection(s"user/conversation/$conversationId")
.resolveOne()
.recover { case _:Exception =>
context.actorOf(Conversation.props(conversationId),conversationId.toString)
}
}
2. Make it Unit and have it send the ActorRef back to your current actor
Pretty much the same as the above, but now you we pipe the future back the current actor, so that the resolved actor can be dealt with in the context of the calling actor's receive loop:
def getOrCreateConversation(conversationId: UUID): Unit {
context.actorSelection(s"user/conversation/$conversationId")
.resolveOne()
.recover { case _:Exception =>
context.actorOf(Conversation.props(conversationId),conversationId.toString)
}.pipeTo(self)
}
3. Create a router actor that you send your Id'ed messages to and it creates/resolves the child and forwards the message
I say that this is likely the correct way, since your goal seems to be cheap lookup at a specific named path. The example you give makes the assumption that the function is always called from within the actor at path /user/conversation otherwise the context.actorOf would not create the child at /user/conversation/{id}/.
Which is to say that you have a router pattern on your hands and the child you create is already known to the router in its child collection. This pattern assumes you have an envelope around any conversation message, something like this:
case class ConversationEnv(id: UUID, msg: Any)
Now all conversation messages get sent to the router instead of to the conversation child directly. The router can now look up the child in its child collection:
def receive = {
case ConversationEnv(id,msg) =>
val conversation = context.child(id.toString) match {
case None => context.actorOf(Conversation.props(id),id.toString)
case Some(x) => x
}
conversation forward msg
}
The additional benefit is that your router is also the conversation supervisor, so if the conversation child dies, it can deal with it. Not exposing the child ActorRef to the outside world also has the benefit that you could have it die when idle and have it get re-created on the next message receipt, etc.

on demand actor get or else create

I can create actors with actorOf and look them with actorFor. I now want to get an actor by some id:String and if it doesnt exist, I want it to be created. Something like this:
def getRCActor(id: String):ActorRef = {
Logger.info("getting actor %s".format(id))
var a = system.actorFor(id)
if(a.isTerminated){
Logger.info("actor is terminated, creating new one")
return system.actorOf(Props[RC], id:String)
}else{
return a
}
}
But this doesn't work as isTerminated is always true and I get actor name 1 is not unique! exception for the second call. I guess I am using the wrong pattern here. Can someone help how to achieve this? I need
Create actors on demand
Lookup actors by id and if not present create them
Ability to destroy on, as I don't know if I will need it again
Should I use a Dispatcher or Router for this?
Solution
As proposed I use a concrete Supervisor that holds the available actors in a map. It can be asked to provide one of his children.
class RCSupervisor extends Actor {
implicit val timeout = Timeout(1 second)
var as = Map.empty[String, ActorRef]
def getRCActor(id: String) = as get id getOrElse {
val c = context actorOf Props[RC]
as += id -> c
context watch c
Logger.info("created actor")
c
}
def receive = {
case Find(id) => {
sender ! getRCActor(id)
}
case Terminated(ref) => {
Logger.info("actor terminated")
as = as filterNot { case (_, v) => v == ref }
}
}
}
His companion object
object RCSupervisor {
// this is specific to Playframework (Play's default actor system)
var supervisor = Akka.system.actorOf(Props[RCSupervisor])
implicit val timeout = Timeout(1 second)
def findA(id: String): ActorRef = {
val f = (supervisor ? Find(id))
Await.result(f, timeout.duration).asInstanceOf[ActorRef]
}
...
}
I've not been using akka for that long, but the creator of the actors is by default their supervisor. Hence the parent can listen for their termination;
var as = Map.empty[String, ActorRef]
def getRCActor(id: String) = as get id getOrElse {
val c = context actorOf Props[RC]
as += id -> c
context watch c
c
}
But obviously you need to watch for their Termination;
def receive = {
case Terminated(ref) => as = as filterNot { case (_, v) => v == ref }
Is that a solution? I must say I didn't completely understand what you meant by "terminated is always true => actor name 1 is not unique!"
Actors can only be created by their parent, and from your description I assume that you are trying to have the system create a non-toplevel actor, which will always fail. What you should do is to send a message to the parent saying “give me that child here”, then the parent can check whether that currently exists, is in good health, etc., possibly create a new one and then respond with an appropriate result message.
To reiterate this extremely important point: get-or-create can ONLY ever be done by the direct parent.
I based my solution to this problem on oxbow_lakes' code/suggestion, but instead of creating a simple collection of all the children actors I used a (bidirectional) map, which might be beneficial if the number of child actors is significant.
import play.api._
import akka.actor._
import scala.collection.mutable.Map
trait ResponsibleActor[K] extends Actor {
val keyActorRefMap: Map[K, ActorRef] = Map[K, ActorRef]()
val actorRefKeyMap: Map[ActorRef, K] = Map[ActorRef, K]()
def getOrCreateActor(key: K, props: => Props, name: => String): ActorRef = {
keyActorRefMap get key match {
case Some(ar) => ar
case None => {
val newRef: ActorRef = context.actorOf(props, name)
//newRef shouldn't be present in the map already (if the key is different)
actorRefKeyMap get newRef match{
case Some(x) => throw new Exception{}
case None =>
}
keyActorRefMap += Tuple2(key, newRef)
actorRefKeyMap += Tuple2(newRef, key)
newRef
}
}
}
def getOrCreateActorSimple(key: K, props: => Props): ActorRef = getOrCreateActor(key, props, key.toString)
/**
* method analogous to Actor's receive. Any subclasses should implement this method to handle all messages
* except for the Terminate(ref) message passed from children
*/
def responsibleReceive: Receive
def receive: Receive = {
case Terminated(ref) => {
//removing both key and actor ref from both maps
val pr: Option[Tuple2[K, ActorRef]] = for{
key <- actorRefKeyMap.get(ref)
reref <- keyActorRefMap.get(key)
} yield (key, reref)
pr match {
case None => //error
case Some((key, reref)) => {
actorRefKeyMap -= ref
keyActorRefMap -= key
}
}
}
case sth => responsibleReceive(sth)
}
}
To use this functionality you inherit from ResponsibleActor and implement responsibleReceive. Note: this code isn't yet thoroughly tested and might still have some issues. I ommited some error handling to improve readability.
Currently you can use Guice dependency injection with Akka, which is explained at http://www.lightbend.com/activator/template/activator-akka-scala-guice. You have to create an accompanying module for the actor. In its configure method you then need to create a named binding to the actor class and some properties. The properties could come from a configuration where, for example, a router is configured for the actor. You can also put the router configuration in there programmatically. Anywhere you need a reference to the actor you inject it with #Named("actorname"). The configured router will create an actor instance when needed.