Search code examples
javaeclipseakkaactor

Sending messages between siblings in Java Akka


In Java, using Akka, I have a Main class which creates two actors (ActorA and ActorB). I can send messages from Main to the Actors, but how do I send messages between the two actors.

Main.java:

public class Main {

    public static void main(String[] args) {

        // Create the ‘actorTest’ actor system.
        final ActorSystem system = ActorSystem.create("actorTest");

        // Create the actors.
        ActorRef ActorRefA = system.actorOf(Props.create(ActorA.class));
        ActorRef ActorRefB = system.actorOf(Props.create(ActorB.class));

        // Send a message to ActorA.
        ActorRefA.tell("message A", ActorRefA);

     }

}

ActorA.java:

public class ActorA extends UntypedActor {

    @Override
    public void onReceive(Object message) throws Exception {
        System.out.println("ActorA received: " + message);

        // This is the line which I can't get to work:
        ActorRefB.tell("message B", self());

    }

}

As you've probably guessed I'm new to this, so any helpful links will be gratefully received. Thanks.


Solution

  • Assuming the actors both need to know about each other you have a couple of options that spring to mind:

    1. Inject the ActorRef of each actor into the other by passing a message from Main to each with the other's ActorRef
    2. Giving the ActorA and ActorB explicit names and sending a message to the other actor using their ActorPaths

    Option 1

    After creation of ActorA and ActorB in main, send the ActorRef of each to the other e.g.

    public class Main {
    
    public static void main(String[] args) {
    
        // Create the ‘actorTest’ actor system.
        final ActorSystem system = ActorSystem.create("actorTest");
    
        // Create the actors.
        ActorRef ActorRefA = system.actorOf(Props.create(ActorA.class));
        ActorRef ActorRefB = system.actorOf(Props.create(ActorB.class));
    
       // Inject ActorRefs
       ActorRefA.tell(ActorRefB, ActorRef.noSender());
       ActorRefB.tell(ActorRefA, ActorRef.noSender());
    
        // Send a message to ActorA.
        ActorRefA.tell("message A", ActorRefA);
    
     }
    
    }
    

    You will also need your ActorA and ActorB implementations to handle a message of this type e.g.

    public class ActorA extends UntypedActor {
    
        private ActorRef actorRefB;
    
        @Override
        public void onReceive(Object message) throws Exception {
            System.out.println("ActorA received: " + message);
    
            if (message instanceof ActorRef) {
                actorRefB = message;
            } else if (message instanceof String) {
                // This is the line which I can't get to work:
                actorRefB.tell("message B", self());
            }
    
        }
    
    }
    

    You would obviously want more sophisticated checks on the variables and sequencing of events to prevent errors but hopefully you get the jist of it.

    Option 2

    If you name your actors explicitly on creation then you will be able to address them more reliably at their ActorPath using ActorSelection

    Your Main class would become:

    public class Main {
    
        public static void main(String[] args) {
    
            // Create the ‘actorTest’ actor system.
            final ActorSystem system = ActorSystem.create("actorTest");
    
            // Create the actors.
            ActorRef ActorRefA = system.actorOf(Props.create(ActorA.class), "actorA");
            ActorRef ActorRefB = system.actorOf(Props.create(ActorB.class), "actorB");
    
            // Send a message to ActorA.
            ActorRefA.tell("message A", ActorRefA);
    
         }
    
    }
    

    And then in your ActorA you would use the Actor Selection to address the other actor e.g.

    public class ActorA extends UntypedActor {
    
        @Override
        public void onReceive(Object message) throws Exception {
            System.out.println("ActorA received: " + message);
    
            ActorSelection actorB = getContext().actorSelection("../actorB");
            actorB.tell("message B", self());
    
        }
    
    }
    

    It should be noted that there are no guarantees when using Actor Selection that the actor you're addressing actually exists. If you need that then see the following link for details of how to achieve it: http://doc.akka.io/docs/akka/2.4.2/java/untyped-actors.html#Identifying_Actors_via_Actor_Selection