Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Caveats of Migrating to Akka's Typed API

DZone's Guide to

Caveats of Migrating to Akka's Typed API

This collection of rules can help you migrate more smoothly to Akka's Typed API, including props, lifecycle hooks, and other features.

Free Resource

Share, secure, distribute, control, and monetize your APIs with the platform built with performance, time-to-value, and growth in mind. Free 90-day trial of 3Scale by Red Hat

Akka’s new Typed API has been released and it finally brings some type safety to the core actor API. When first trying to work with it, it wasn’t clear right away how certain concepts from untyped API map to this new API. Here are some caveats which can be helpful when migrating or incorporating it into your codebase.

Interaction Between Typed and Untyped

In order for typed and untyped codebases to coexist, first thing is to import a special adapter to convert between the two: 
import akka.typed.scaladsl.adapter._

It adds a number of various extension methods that help to convert between typed and untyped versions of ActorRefActorSystemActorContext and so on

val sys: akka.actor.ActorSystem = ctx.system.toUntyped or ctx.self.toUntyped

Creating Actors

Where are Props?

The role of the Props has slightly changed.

The first thing to notice is that there is now a method named spawn available for typed ActorSystem and ActorContext and it receives Props which are optional now.

So Props are still here but their use case has changed: In untyped API they were usually used to carry actor’s initialization arguments along with deployment related options. 
Typed Props on the other hand are exclusively used for setting dispatchers to run actors on and configuring their mailboxes (i.e. deployment config) which is a much cleaner approach because it does only one thing.

Instead of old Props now you have functions that take arguments and create Behaviors(that is the main thing you pass to spawn method). Not to mention the approach is type safe and there won’t be any nasty ‘constructor not found’ runtime exceptions when incompatible arguments been passed.

Lifecycle

How do I specify lifecycle hooks?

There are no lifecycle hooks in the typed API, instead they are modeled as special signal messages that can be handled by actors similarly to regular messages. The handling part is described in onSignal that Actor.immutable has. The signals are PostStopPreRestart and Terminated.

    Actor.immutable[MyProtocol] { (ctx, msg) => msg match {
            case _=> Actor.same
        }
    } onSignal {
      case (c, PostStop) =>
        c.system.log.info("Worker {} is STOPPED", ctx.self)
        Actor.same
    }


Where is def preStart?

You may have noticed that there is no PreStart event, but how do we get the same pre initialization functionality?

We can’t use onSignal but instead, we can to wrap a behavior with Actor.deferredand do all the initialization there.

Supervision

Supervision Strategy

When spawning an actor, wrap the behavior with Actor.supervise and use supervision strategy.

ctx.spawn(Actor.supervise(ApplicationManager.applicationManager(config))
  .onFailure[RuntimeException](
    SupervisorStrategy.restartWithBackoff(3.seconds, 20.second, 0.2)), "manager")


When actor throws an exception, it triggers supervision.

But if the actor creates a future and the future fails, to force restart of the actor on future fail, use

future.failed.foreach { t => self ! Failed(t) }

where Failed is part of the protocol and self is val self = ctx.self . Handle it with:

Actor.immutable[MyProtocol] { (ctx, msg) =>
  msg match {
    case Failed(t) =>
      throw new Exception(t)
      Actor.stopped
  }
}


Where is context.stop(self)? 
Use Actor.stopped as a last statement for your message handling part in the actor.

What Is a spawnAdapter Method and When Is It Useful?

Maybe it is because Scala is currently lacking proper union types?

We use spawnAdapter to handle messages from "outside" that are not part of the protocol (and map them to your protocol).

So the idea is that when you are exposing your typed ref as untyped and a message that can be sent to it is not a part of actor’s protocol, then the adapter maps this message to your actor protocol.

It needs to be in the Actor.deferred block where ctx is the context.

ctx.spawnAdapter[WebSocketTermination]((_: WebSocketTermination) => TextMessage.Strict("DIED"))

In this example, the actor ref has a protocol of web socket messages used as a Sink for a websocket client, and its ref is exposed as untyped. When the socket connection goes down, it receives a WebSocketTermination message, and with the help of the adapter, it gets mapped onto a regular TextMessage.Strict("DIED") which can be handled properly because it is a part of the protocol.

Explore the core elements of owning an API strategy and best practices for effective API programs. Download the API Owner's Manual, brought to you by 3Scale by Red Hat

Topics:
akka ,integration ,api

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}