Creating Actors

Creating Actors #

Akka provides different APIs to program with actors. In this section, we look at the typed object-oriented API by discussing unit tests defined in ObjectOrientedActorTests that demonstrate how to use it. Akka provides a helper class to manage actors in tests. We instantiate it as follows.

static final TestKitJunitResource KIT = new TestKitJunitResource();

Sending and receiving messages #

Our first test uses the test kit to spawn an actor that stops after receiving the first message.

@Test
public void testThatTerminatorStopsOnMessage() {
    ActorRef<Object> terminator = KIT.spawn(Terminator.create());
    terminator.tell("");
    KIT.createTestProbe().expectTerminated(terminator);
}

In Akka, actors are created from Behavior instances and the method tell on actor references is used to send a message to an actor. In object-oriented style, Behaviors are defined as sub-classes of AbstractBehavior. For simplicity, we define a static nested class.

static class Terminator extends AbstractBehavior<Object> {
    static Behavior<Object> create() {
        return Behaviors.setup(Terminator::new);
    }

    Terminator(ActorContext<Object> ctx) {
        super(ctx);
    }

    @Override
    public Receive<Object> createReceive() {
        return newReceiveBuilder() //
            .onAnyMessage(msg -> Behaviors.stopped()) //
            .build();
    }
}

The type parameter is for corresponding messages. For this test, we do not care about this type and use Object. By convention, behaviour classes define a static factory method create which is used to instantiate behaviors. It should be defined using Behaviors.setup to call the super-class constructor with the provided ActorContext.

Object-oriented behaviors specify how to respond to messages by overriding the createReceive method, which uses the builder pattern to register message handlers. Handlers can return different behaviors that are used subsequently. In this case we use Behaviors.stopped to stop responding.

Custom message types #

As a best practice, behavior classes should define types for corresponding messages as nested classes. The following behavior defines two such types.

static class Bouncer extends AbstractBehavior<Ping> {
    static class Ping {
        final ActorRef<Pong> sender;

        Ping(ActorRef<Pong> sender) {
            this.sender = sender;
        }
    }

    static class Pong {
    }

    static Behavior<Ping> create() {
        return Behaviors.setup(Bouncer::new);
    }

    Bouncer(ActorContext<Ping> ctx) {
        super(ctx);
    }

    @Override
    public Receive<Ping> createReceive() {
        return newReceiveBuilder().onAnyMessage(ping -> {
            ping.sender.tell(new Pong());
            return this;
        }).build();
    }
}

The Ping message wraps a reference to the actor that sent the message. The Pong message is used to respond to pings. When receiving a ping, actors with this behavior respond to the actor wrapped in the message. Instead of stopping, the handler returns the same behavior in order to continue processing pings indefinitely.

The following test spawns an actor with this behavior.

@Test
public void testThatBouncerBounces() {
    ActorRef<Ping> bouncer = KIT.spawn(Bouncer.create());
    TestProbe<Pong> probe = KIT.createTestProbe();
    IntStream.range(0, 5).forEach(i -> {
        bouncer.tell(new Ping(probe.getRef()));
        assertNotNull(probe.receiveMessage());
    });
}

Several messages are sent to the actor, and each time the sender gets a response.

Task: Add handshake test #

Add a new behavior Handshake defined as a nested class inside ObjectOrientedActorTests. Define a corresponding message type Self wrapping a reference to an actor participating in the handshake. Actors using the Handshake behavior should answer Self messages with another Self message, sending their own reference to the wrapped reference and then continue waiting for more handshakes. In object-oriented behaviors, the own actor reference is available via getContext().getSelf().

Write a test demonstrating that handshakes are answered.