I am trying to get "ask" working for Akka Typed. I have followed examples online, and I thought I pretty much replicated what they showed, but I'm getting an compiler error when I try to evaluate the response from the "ask". Here's my minimal reproducible example.
SuperSimpleAsker is an actor that is requesting a "widget" from the MyWidgetKeeper actor. The response is a string representing the widget's id. All I'm trying to do so far is log the received widget id as a "Success" message, and will add more stuff to do with the id later. When the SuperSimpleAsker is created, the ActorRef of the MyWidgetKeeper is passed in. I have left out the Main program that creates the actors to keep the code simple.
The error that I get is:
type mismatch;
found : Unit
required: widgets.SuperSimpleAsker.Request
This error occurs on both of the logger.* lines (inside of the Case Failure and Case Success blocks toward the end of the code listing).
I don't understand what part of the code is requiring a "widgets.SuperSimpleAsker.Request" object or why.
package widgets
import scala.concurrent.duration.DurationInt
import scala.util.{Failure, Success}
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, Behavior}
import akka.util.Timeout
import com.typesafe.scalalogging.LazyLogging
object MyWidgetKeeper {
sealed trait Request
case class GetWidget(replyTo: ActorRef[Response]) extends Request
sealed trait Response
case class WidgetResponse(widget: String) extends Response
def apply(): Behavior[Request] =
new MyWidgetKeeper().myWidgetKeeper()
}
class MyWidgetKeeper {
import MyWidgetKeeper._
def myWidgetKeeper(): Behavior[Request] = {
Behaviors.receive { (context, message) =>
message match {
case GetWidget(replyTo) =>
replyTo ! WidgetResponse("12345")
Behaviors.same
}
}
}
}
object SuperSimpleAsker {
sealed trait Request
case object DoStuff extends Request
def apply(widgetKeeper: ActorRef[MyWidgetKeeper.Request]): Behavior[Request] =
new SuperSimpleAsker(widgetKeeper).simpleAsker()
}
class SuperSimpleAsker(widgetKeeper: ActorRef[MyWidgetKeeper.Request]) extends LazyLogging{
import SuperSimpleAsker._
import widgets.MyWidgetKeeper.GetWidget
private def simpleAsker(): Behavior[Request] = {
Behaviors.receive { (context, message) =>
message match {
case DoStuff =>
logger.info(f"Doing stuff")
implicit val timeout = Timeout(2000 millis)
context.ask(widgetKeeper, GetWidget)
{
case Failure(exception) =>
logger.error(f"Failed: ${exception.getMessage}")
case Success(response: MyWidgetKeeper.Response) =>
response match {
case MyWidgetKeeper.WidgetResponse(id) =>
logger.info(f"Success: Got Widget# $id")
// Do some more stuff with the widget id
}
}
Behaviors.same
}
}
}
}
CodePudding user response:
In Akka Typed's context.ask
, the passed function converts the successful or failed ask into a message which gets sent to the actor, ideally without performing a side effect.
So your SuperSimpleAsker
will have to add messages that the ask can be converted to:
object SuperSimpleAsker {
sealed trait Request
case object DoStuff extends Request
case class WidgetResponseFor(widgetId: String) extends Request
case object NoWidgetResponse extends Request
def apply(widgetKeeper: ActorRef[MyWidgetKeeper.Request]): Behavior[Request] =
new SuperSimpleAsker(widgetKeeper).simpleAsker()
}
class SuperSimpleAsker(widgetKeeper: ActorRef[MyWidgetKeeper.Request]) extends LazyLogging{
import SuperSimpleAsker._
import widgets.MyWidgetKeeper.GetWidget
private def simpleAsker(): Behavior[Request] = {
Behaviors.receive { (context, message) =>
message match {
case DoStuff =>
logger.info(f"Doing stuff")
implicit val timeout = Timeout(2000 millis)
context.ask(widgetKeeper, GetWidget)
{
case Failure(_) => // there's actually only one possible exception: timed out
NoWidgetResponse
case Success(response: MyWidgetKeeper.Response) =>
WidgetResponseFor(response.widget)
}
Behaviors.same
case WidgetResponseFor(id) =>
logger.info(f"Success: Got Widget# $id")
// Do stuff with the widget id
Behaviors.same
case NoWidgetResponse =>
logger.error("Failed")
Behaviors.same
}
}
}
}