Reputation: 792
I'm trying to migrate a personal project from Akka Classic to Akka Typed and I'm having some troubles with messageAdapters
.
Here below the code to reproduce the behavior that is causing me some troubles:
object Example extends App {
val system = ActorSystem(MyActor(), "system")
system ! MyActor.Msg("Foo") // 3 chars lenght
system ! MyActor.Msg("FooBarOsdAsd") // 12 chars lenght
object MyActor {
sealed trait Command
case class Msg(payload: String) extends Command
case class Question(payload: String, replyTo: ActorRef[Response]) extends Command
case class WrappedResponseOne(response: Response) extends Command
case class WrappedResponseTwo(response: Response) extends Command
sealed trait Response
case object Yes extends Response
case object No extends Response
def apply(): Behavior[Command] =
Behaviors.setup { context =>
new MyActor(context).stateless()
}
}
// I need to have a private class that implement a method to expose the wanted behavior
private class MyActor(context: ActorContext[MyActor.Command]) {
// This is the adapter I want to use
private val wrappedOne: ActorRef[MyActor.Response] =
context.messageAdapter[MyActor.Response](MyActor.WrappedResponseOne)
// This variable is unused
private val wrappedTwo: ActorRef[MyActor.Response] =
context.messageAdapter[MyActor.Response](MyActor.WrappedResponseTwo)
def stateless(): Behaviors.Receive[MyActor.Command] = Behaviors.receiveMessage[MyActor.Command] {
case MyActor.Msg(payload) =>
context.log.debug(s"$payload")
context.self ! MyActor.Question(payload, wrappedOne)
Behaviors.same
case MyActor.Question(payload, replyTo) =>
if (payload.length > 10) replyTo ! MyActor.No else replyTo ! MyActor.Yes
Behaviors.same
case MyActor.WrappedResponseOne(response) =>
response match {
case MyActor.Yes =>
context.log.info("YES")
Behaviors.same
case MyActor.No =>
context.log.info("NO")
Behaviors.same
}
case msg =>
context.log.debug(s"$msg")
Behaviors.unhandled
}
}
}
Here I'm defining two messageAdapters
: wrappedOne
and wrappedTwo
. When the actor got a MyActor.Msg
I want that the responses will be handled by wrappedOne
(leaving wrappedTwo
completely unused). I can't get why my code let wrappedTwo
manage the responses. Here below is the output from the console.
SLF4J: See also http://www.slf4j.org/codes.html#substituteLogger
22:57:38.879 [system-akka.actor.default-dispatcher-3] DEBUG exlude.ActorAdapter$MyActor - Foo
22:57:38.881 [system-akka.actor.default-dispatcher-3] DEBUG exlude.ActorAdapter$MyActor - FooBarOsdAsd
22:57:38.890 [system-akka.actor.default-dispatcher-3] DEBUG exlude.ActorAdapter$MyActor - WrappedResponseTwo(Yes)
22:57:38.891 [system-akka.actor.default-dispatcher-3] DEBUG exlude.ActorAdapter$MyActor - WrappedResponseTwo(No)
22:57:38.904 [system-akka.actor.default-dispatcher-3] INFO akka.actor.LocalActorRef - Message [exlude.ActorAdapter$MyActor$WrappedResponseTwo] to Actor[akka://system/user] was unhandled. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
22:57:38.907 [system-akka.actor.default-dispatcher-3] INFO akka.actor.LocalActorRef - Message [exlude.ActorAdapter$MyActor$WrappedResponseTwo] to Actor[akka://system/user] was unhandled. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
Is there something wrong with how I'm defining the adapters?
Upvotes: 0
Views: 273
Reputation: 20561
That behavior is by design: only one message adapter for a given incoming message type can be registered at a time. This prevents unbounded growth in the number of adapters if (e.g.) the adapter is being registered in response to messages. The last adapter wins.
See docs and note that if A
is a subclass of B
, adapters for A
and B
can both be registered, but since the adapters are tested last-registered-first, if the B
adapter is registered after the A
adapter, it will shadow the A
adapter.
For situations where there's at-most-one response per request, context.ask
may be a better fit.
If you really need multiple adaptations of the same message types, spawning a child actor for each adaptation is probably the way to go; having to do this may be a sign that your messaging protocols could use a rethink.
Upvotes: 1