So I have 2 actors, ClientIn
and Maker
.
I need to have a reference of Maker inside of ClientIn, so I can forward some messages that ClientIn
recieves to Maker
.
I added Maker
to the receptionist
, but since clientIn
is created first, and maker
2nd I'm not sure how to query to fetch the actorRef.
Actually I'm just confused how I can now query the receptionist so that clientIn will get the reference to the Maker actor.
Here is what I have:
// guardian actor constructor:
Behaviors.setup[Any] { context =>
implicit val ec: ExecutionContext = context.executionContext
val system = context.system
val clientIn = context.spawn(ClientIn(), "ClientIn")
val maker = context.spawn(Maker(clientOut), "mmaker")
....
}
ClientIn.scala:
object ClientIn {
sealed trait ClientInCommand
case class Watch(symbol: String) extends ClientInCommand
case class Command(value: String) extends ClientInCommand
private case class ListingResponse(listing: Receptionist.Listing)
extends ClientInCommand
def apply(): Behavior[ClientInCommand] = {
Behaviors.setup { context =>
context.log.info(s"ClientIn starting...${context.self}")
val listingResponseAdapter =
context.messageAdapter[Receptionist.Listing](ListingResponse.apply)
var maker: Option[ActorRef[Maker.MMCommand]] = None
Behaviors.receiveMessage[ClientInCommand] {
case Watch(symbol) =>
context.log.info(s"ClientIn watch message received..$symbol")
Behaviors.same
case Command(value) =>
context.log.info(s"ClientInCommand.Command with value:$value")
// TODO: Maker ! RecieveCommand(value)
Behaviors.same
case ListingResponse(Maker.makerKey.Listing(listings)) =>
maker = Some(listings.head)
Behaviors.same
}
}
}
}
Maker.scala:
object Maker {
val makerKey = ServiceKey[MMCommand]("makerKey")
sealed trait MMCommand
case object Startup extends MMCommand
case object Shutdown extends MMCommand
def apply(): Behavior[MMCommand] = {
Behaviors.setup { context =>
context.system.receptionist ! Receptionist.Register(
makerKey,
context.self
)
Behaviors.receiveMessage[MMCommand] {
case Startup =>
context.log.info("Maker startup message received..")
Behaviors.same
case Shutdown =>
context.log.info("Maker shutdown message received..")
Behaviors.same
}
}
}
}
You can send a Receptionist.Subscribe(Maker.makerKey)
message to the receptionist, as described in the Akka documentation on Actor discovery:
context.system.receptionist ! Receptionist.Subscribe(Maker.makerKey, listingResponseAdapter)
Quoting the documentation:
It will send Listing messages to the subscriber, first with the set of entries upon subscription, then whenever the entries for a key are changed.
This allows it to receive the maker ActorRef
even if it is registered with the receptionist after the ClientIn
actor is spawned. However, it is possible in principle that ClientIn
may receive other messages such as Command
before this happens. It will need to handle these cases, perhaps by stashing or dropping commands until the Maker
has been registered.
In this specific example, it would be simpler to avoid this requirement by spawning the Maker
first, and passing its ActorRef
to ClientIn.apply
:
val maker = context.spawn(Maker(), "mmaker")
val clientIn = context.spawn(ClientIn(maker), "ClientIn")
Then, in ClientIn
:
def apply(maker: ActorRef[Maker.MMCommand]): Behavior[ClientInCommand] = {
Behaviors.setup { context =>
context.log.info(s"ClientIn starting...${context.self}")
Behaviors.receiveMessage[ClientInCommand] {
case Watch(symbol) =>
context.log.info(s"ClientIn watch message received..$symbol")
Behaviors.same
case Command(value) =>
context.log.info(s"ClientInCommand.Command with value:$value")
maker ! Maker.ReceiveCommand(value)
Behaviors.same
}
}
}
Note that the ActorRef
returned from spawn
is able to receive messages immediately, even if the actor hasn't fully initialized.