Coexistence

Dependency

To use Akka Actor Typed, you must add the following dependency in your project:

sbt
libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % "2.5.16"
Maven
<dependency>
  <groupId>com.typesafe.akka</groupId>
  <artifactId>akka-actor-typed_2.11</artifactId>
  <version>2.5.16</version>
</dependency>
Gradle
dependencies {
  compile group: 'com.typesafe.akka', name: 'akka-actor-typed_2.11', version: '2.5.16'
}

Introduction

We believe Akka Typed will be adopted in existing systems gradually and therefore it’s important to be able to use typed and untyped actors together, within the same ActorSystem. Also, we will not be able to integrate with all existing modules in one big bang release and that is another reason for why these two ways of writing actors must be able to coexist.

There are two different ActorSystems: akka.actor.ActorSystem and akka.actor.typed.ActorSystem.

Currently the typed actor system is implemented using an untyped actor system under the hood. This may change in the future.

Typed and untyped can interact the following ways:

  • untyped actor systems can create typed actors
  • typed actors can send messages to untyped actors, and opposite
  • spawn and supervise typed child from untyped parent, and opposite
  • watch typed from untyped, and opposite
  • untyped actor system can be converted to a typed actor system

In the examples the akka.actor package is aliased to untyped.

Scala
import akka.{ actor ⇒ untyped }
Full source at GitHub

The examples use fully qualified class names for the untyped classes to distinguish between typed and untyped classes with the same name.

Untyped to typed

While coexisting your application will likely still have an untyped ActorSystem. This can be converted to a typed ActorSystem so that new code and migrated parts don’t rely on the untyped system:

Scala
val system = akka.actor.ActorSystem("UntypedToTypedSystem")
val typedSystem: ActorSystem[Nothing] = system.toTyped
Full source at GitHub
Java
akka.actor.ActorSystem untypedActorSystem = akka.actor.ActorSystem.create();
ActorSystem<Void> typedActorSystem = Adapter.toTyped(untypedActorSystem);
Full source at GitHub

Then for new typed actors here’s how you create, watch and send messages to it from an untyped actor.

Scala
object Typed {
  sealed trait Command
  final case class Ping(replyTo: ActorRef[Pong.type]) extends Command
  case object Pong

  val behavior: Behavior[Command] =
    Behaviors.receive { (ctx, msg) ⇒
      msg match {
        case Ping(replyTo) ⇒
          println(s"${ctx.self} got Ping from $replyTo")
          // replyTo is an untyped actor that has been converted for coexistence
          replyTo ! Pong
          Behaviors.same
      }
    }
}
Full source at GitHub
Java
public static abstract class Typed {
  interface Command { }

  public static class Ping implements Command {
    public final akka.actor.typed.ActorRef<Pong> replyTo;

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

  public static class Pong { }

  public static Behavior<Command> behavior() {
    return Behaviors.receive(Typed.Command.class)
      .onMessage(Typed.Ping.class, (ctx, msg) -> {
        msg.replyTo.tell(new Pong());
        return same();
      })
      .build();
  }
}
Full source at GitHub

The top level untyped actor is created in the usual way:

Scala
val untypedActor = system.actorOf(Untyped.props())
Full source at GitHub
Java
akka.actor.ActorSystem as = akka.actor.ActorSystem.create();
akka.actor.ActorRef untyped = as.actorOf(Untyped.props());
Full source at GitHub

Then it can create a typed actor, watch it, and send a message to it:

Scala
class Untyped extends untyped.Actor {
  // context.spawn is an implicit extension method
  val second: ActorRef[Typed.Command] =
    context.spawn(Typed.behavior, "second")

  // context.watch is an implicit extension method
  context.watch(second)

  // self can be used as the `replyTo` parameter here because
  // there is an implicit conversion from akka.actor.ActorRef to
  // akka.actor.typed.ActorRef
  second ! Typed.Ping(self)

  override def receive = {
    case Typed.Pong ⇒
      println(s"$self got Pong from ${sender()}")
      // context.stop is an implicit extension method
      context.stop(second)
    case untyped.Terminated(ref) ⇒
      println(s"$self observed termination of $ref")
      context.stop(self)
  }
}
Full source at GitHub
Java
public static class Untyped extends AbstractActor {
  public static akka.actor.Props props() {
    return akka.actor.Props.create(Untyped.class);
  }

  private final akka.actor.typed.ActorRef<Typed.Command> second =
    Adapter.spawn(getContext(), Typed.behavior(), "second");

  @Override
  public void preStart() {
    Adapter.watch(getContext(), second);
    second.tell(new Typed.Ping(Adapter.toTyped(getSelf())));
  }

  @Override
  public Receive createReceive() {
    return receiveBuilder()
      .match(Typed.Pong.class, msg -> {
        Adapter.stop(getContext(), second);
      })
      .match(akka.actor.Terminated.class, t -> {
        getContext().stop(getSelf());
      })
      .build();
  }
}
Full source at GitHub

There is one import that is needed to make that work. We import the Adapter class and call static methods for conversion.

Scala
// adds support for typed actors to an untyped actor system and context
import akka.actor.typed.scaladsl.adapter._
Full source at GitHub
Java
// In java use the static methods on Adapter to convert from typed to untyped
import akka.actor.typed.javadsl.Adapter;
Full source at GitHub

That adds some implicit extension methods that are added to untyped and typed ActorSystem and ActorContext in both directions. To convert between typed and untyped there are adapter methods in akka.actor.typed.javadsl.Adapter. Note the inline comments in the example above.

Typed to untyped

Let’s turn the example upside down and first start the typed actor and then the untyped as a child.

The following will show how to create, watch and send messages back and forth from a typed actor to this untyped actor:

Scala
object Untyped {
  def props(): untyped.Props = untyped.Props(new Untyped)
}
class Untyped extends untyped.Actor {
  override def receive = {
    case Typed.Ping(replyTo) ⇒
      replyTo ! Typed.Pong
  }
}
Full source at GitHub
Java
public static class Untyped extends AbstractActor {
  public static akka.actor.Props props() {
    return akka.actor.Props.create(Untyped.class);
  }

  @Override
  public Receive createReceive() {
    return receiveBuilder()
      .match(Typed.Ping.class, msg -> {
        msg.replyTo.tell(new Typed.Pong());
      })
      .build();
  }
}
Full source at GitHub

Creating the actor system and the typed actor:

Scala
val system = untyped.ActorSystem("TypedWatchingUntyped")
val typed = system.spawn(Typed.behavior, "Typed")
Full source at GitHub
Java
ActorSystem as = ActorSystem.create();
ActorRef<Typed.Command> typed = Adapter.spawn(as, Typed.behavior(), "Typed");
Full source at GitHub

Then the typed actor creates the untyped actor, watches it and sends and receives a response:

Scala
object Typed {
  final case class Ping(replyTo: akka.actor.typed.ActorRef[Pong.type])
  sealed trait Command
  case object Pong extends Command

  val behavior: Behavior[Command] =
    Behaviors.setup { context ⇒
      // context.spawn is an implicit extension method
      val untyped = context.actorOf(Untyped.props(), "second")

      // context.watch is an implicit extension method
      context.watch(untyped)

      // illustrating how to pass sender, toUntyped is an implicit extension method
      untyped.tell(Typed.Ping(context.self), context.self.toUntyped)

      Behaviors.receivePartial[Command] {
        case (ctx, Pong) ⇒
          // it's not possible to get the sender, that must be sent in message
          // context.stop is an implicit extension method
          ctx.stop(untyped)
          Behaviors.same
      } receiveSignal {
        case (_, akka.actor.typed.Terminated(_)) ⇒
          Behaviors.stopped
      }
    }
}
Full source at GitHub
Java
public static abstract class Typed {
  public static class Ping {
    public final akka.actor.typed.ActorRef<Pong> replyTo;

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

  interface Command { }
  public static class Pong implements Command { }

  public static Behavior<Command> behavior() {
    return akka.actor.typed.javadsl.Behaviors.setup(context -> {
      akka.actor.ActorRef second = Adapter.actorOf(context, Untyped.props(), "second");

      Adapter.watch(context, second);

      second.tell(new Typed.Ping(context.getSelf().narrow()),
        Adapter.toUntyped(context.getSelf()));

      return akka.actor.typed.javadsl.Behaviors.receive(Typed.Command.class)
        .onMessage(Typed.Pong.class, (ctx, msg) -> {
          Adapter.stop(ctx, second);
          return same();
        })
        .onSignal(akka.actor.typed.Terminated.class, (ctx, sig) -> stopped())
        .build();
    });
  }
}
Full source at GitHub

There is one caveat regarding supervision of untyped child from typed parent. If the child throws an exception we would expect it to be restarted, but supervision in Akka Typed defaults to stopping the child in case it fails. The restarting facilities in Akka Typed will not work with untyped children. However, the workaround is to add another untyped actor that takes care of the supervision, i.e. restarts in case of failure if that is the desired behavior.

Found an error in this documentation? The source code for this page can be found here. Please feel free to edit and contribute a pull request.