Documentation

You are viewing the documentation for the 2.8.0-M4 development release. The latest stable release series is 3.0.x.

§Integrating with Akka Typed

§Akka Actor Typed styles

Akka’s Typed Actor API has two styles:

  1. a “functional programming” style, based on defining actor Behavior as values, and
  2. a “object-oriented” style, based on defining the Behavior as a subclass

Here’s an example of a simple actor that says hello back:

Scala FP
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors

object HelloActor {
  final case class SayHello(name: String, replyTo: ActorRef[String])

  def apply(): Behavior[SayHello] = {
    Behaviors.receiveMessage[SayHello] {
      case SayHello(name, replyTo) =>
        replyTo ! s"Hello, $name"
        Behaviors.same
    }
  }
}
Scala OO
import akka.actor.typed.ActorRef
import akka.actor.typed.scaladsl.AbstractBehavior

object HelloActor {
  final case class SayHello(name: String, replyTo: ActorRef[String])
}

final class HelloActor extends AbstractBehavior[HelloActor.SayHello] {
  import HelloActor._
  def onMessage(msg: SayHello) = {
    msg.replyTo ! s"Hello, ${msg.name}"
    this
  }
}
Java
import akka.actor.typed.ActorRef;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.Receive;

public final class HelloActor extends AbstractBehavior<HelloActor.SayHello> {

  public static final class SayHello {
    public final String name;
    public final ActorRef<String> replyTo;

    public SayHello(String name, ActorRef<String> replyTo) {
      this.name = name;
      this.replyTo = replyTo;
    }
  }

  @Override
  public Receive<SayHello> createReceive() {
    return newReceiveBuilder().onMessage(SayHello.class, this::onHello).build();
  }

  private Behavior<SayHello> onHello(SayHello message) {
    message.replyTo.tell("Hello, " + message.name);
    return this;
  }
}

Here’s an example of an actor that depends on Play’s Configuration in order to return configuration values:

Scala FP
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors
import com.google.inject.Provides
import play.api.Configuration
import play.api.libs.concurrent.ActorModule

object ConfiguredActor extends ActorModule {
  type Message = GetConfig

  final case class GetConfig(replyTo: ActorRef[String])

  @Provides
  def apply(configuration: Configuration): Behavior[GetConfig] = {
    Behaviors.setup { _ =>
      val config = configuration.get[String]("my.config")
      Behaviors.receiveMessage[GetConfig] {
        case GetConfig(replyTo) =>
          replyTo ! config
          Behaviors.same
      }
    }
  }
}
Scala OO
import akka.actor.typed.ActorRef
import akka.actor.typed.scaladsl.AbstractBehavior
import javax.inject.Inject
import play.api.Configuration

object ConfiguredActor {
  final case class GetConfig(replyTo: ActorRef[String])
}

final class ConfiguredActor @Inject()(configuration: Configuration)
    extends AbstractBehavior[ConfiguredActor.GetConfig] {
  import ConfiguredActor._
  val config = configuration.get[String]("my.config")
  def onMessage(msg: GetConfig) = {
    msg.replyTo ! config
    this
  }
}
Java
import akka.actor.typed.ActorRef;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.Receive;
import akka.actor.typed.javadsl.Adapter;
import com.typesafe.config.Config;

import javax.inject.Inject;

public final class ConfiguredActor extends AbstractBehavior<ConfiguredActor.GetConfig> {

  public static final class GetConfig {
    public final ActorRef<String> replyTo;

    public GetConfig(ActorRef<String> replyTo) {
      this.replyTo = replyTo;
    }
  }

  private final String config;

  @Inject
  public ConfiguredActor(Config configuration) {
    config = configuration.getString("my.config");
  }

  @Override
  public Receive<GetConfig> createReceive() {
    return newReceiveBuilder().onMessage(GetConfig.class, this::onGetConfig).build();
  }

  private Behavior<GetConfig> onGetConfig(GetConfig message) {
    message.replyTo.tell(config);
    return this;
  }
}

§Compile-time dependency injection

Using compile-time dependency injection for Akka Typed requires creating the actor Behavior value and using it to spawn the actor:

Scala
import akka.actor.typed.scaladsl.adapter._
import play.api._
import play.api.routing.Router

final class AppComponents(context: ApplicationLoader.Context)
    extends BuiltInComponentsFromContext(context)
    with NoHttpFiltersComponents {
  val router = Router.empty

  val helloActor =
    actorSystem.spawn(HelloActor(), "hello-actor")
  val configuredActor =
    actorSystem.spawn(ConfiguredActor(configuration), "configured-actor")

  val main = new Main(helloActor, configuredActor)
}
Java
import akka.actor.typed.ActorRef;
import akka.actor.typed.javadsl.Adapter;
import play.ApplicationLoader;
import play.BuiltInComponentsFromContext;
import play.mvc.EssentialFilter;
import play.routing.Router;

import java.util.Collections;
import java.util.List;

public final class AppComponents extends BuiltInComponentsFromContext {

  public final ActorRef<HelloActor.SayHello> helloActor;
  public final ActorRef<ConfiguredActor.GetConfig> configuredActor;
  public final Main main;

  public AppComponents(ApplicationLoader.Context context) {
    super(context);
    helloActor = Adapter.spawn(actorSystem(), new HelloActor(), "hello-actor");
    configuredActor =
        Adapter.spawn(actorSystem(), new ConfiguredActor(config()), "configured-actor");
    main = new Main(helloActor, configuredActor);
  }

  @Override
  public Router router() {
    return Router.empty();
  }

  @Override
  public List<EssentialFilter> httpFilters() {
    return Collections.emptyList();
  }
}

§Runtime dependency injection

For runtime dependency injection use the “typed” methods in AkkaGuiceSupport.

Given a component in your application or system that needs injecting, like this one:

Scala
import javax.inject.Inject
import javax.inject.Singleton
import akka.actor.typed.ActorRef

@Singleton final class Main @Inject()(
    val helloActor: ActorRef[HelloActor.SayHello],
    val configuredActor: ActorRef[ConfiguredActor.GetConfig],
)
Java
import akka.actor.typed.ActorRef;

import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
public final class Main {
  public final ActorRef<HelloActor.SayHello> helloActor;
  public final ActorRef<ConfiguredActor.GetConfig> configuredActor;

  @Inject
  public Main(
      ActorRef<HelloActor.SayHello> helloActor,
      ActorRef<ConfiguredActor.GetConfig> configuredActor) {
    this.helloActor = helloActor;
    this.configuredActor = configuredActor;
  }
}

You can define a Guice Module defined like so:

Scala FP
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport

object AppModule extends AbstractModule with AkkaGuiceSupport {
  override def configure() = {
    bindTypedActor(HelloActor(), "hello-actor")         // uses apply
    bindTypedActor(ConfiguredActor, "configured-actor") // uses object
  }
}
Scala OO
import com.google.inject.AbstractModule
import play.api.libs.concurrent.AkkaGuiceSupport

object AppModule extends AbstractModule with AkkaGuiceSupport {
  override def configure() = {
    bindTypedActor(classOf[HelloActor], "hello-actor")
    bindTypedActor(classOf[ConfiguredActor], "configured-actor")
  }
}
Java
import com.google.inject.AbstractModule;
import play.libs.akka.AkkaGuiceSupport;

public class AppModule extends AbstractModule implements AkkaGuiceSupport {

  @Override
  protected void configure() {
    bindTypedActor(HelloActor.class, "hello-actor");
    bindTypedActor(ConfiguredActor.class, "configured-actor");
  }
}

Next: Contributing to Play