sss.openstar.message.MessageInBoxActors.scala Maven / Gradle / Ivy
package sss.openstar.message
import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Props}
import sss.openstar.account.NodeIdentity
import sss.openstar.chains.Chains.GlobalChainIdMask
import sss.openstar.message.MessageInBoxActors.{CreateMessageInBoxActor, MessageInBoxActorTerminated, StopMessageInBoxActor}
import sss.openstar.wallet.UnlockedWallet
import sss.openstar.{Currency, UniqueNodeIdentifier}
class MessageInBoxActors[C <: Currency](childConstructor: MessageInBoxActorConstructor[C])
extends Actor
with ActorLogging {
override def receive: Receive = handleChildrenLifecycle(Set.empty, Map.empty)
private def handleChildrenLifecycle(
childrenBeingStopped: Set[UniqueNodeIdentifier],
stash: Map[UniqueNodeIdentifier, CreateMessageInBoxActor[C]]
): Receive = {
case msg: CreateMessageInBoxActor[C @unchecked] =>
import msg._
val childName = MessageInBoxActor.name(nodeIdentity.id)
if (context.child(childName).isEmpty) createAndWatchMessageInBoxActor(childrenBeingStopped, stash, msg)
else if (childrenBeingStopped.contains(nodeIdentity.id)) stashCreateActorMessage(childrenBeingStopped, stash, msg)
else
log.warning(
s"Request to create MessageInBoxActor for user ${nodeIdentity.id} has been received while the old instance is still running"
)
case StopMessageInBoxActor(userId) =>
val childName = MessageInBoxActor.name(userId)
context.child(childName).foreach { child =>
context.stop(child)
context.become(handleChildrenLifecycle(childrenBeingStopped + userId, stash))
}
case MessageInBoxActorTerminated(userId) =>
stash.get(userId).foreach(self ! _)
context.become(handleChildrenLifecycle(childrenBeingStopped - userId, stash - userId))
}
private def stashCreateActorMessage(
childrenBeingStopped: Set[UniqueNodeIdentifier],
stash: Map[UniqueNodeIdentifier, CreateMessageInBoxActor[C]],
msg: CreateMessageInBoxActor[C]
): Unit = {
log.warning(
s"Request to create MessageInBoxActor for user ${msg.nodeIdentity.id} has been received while the old instance is still being stopped"
)
context.become(handleChildrenLifecycle(childrenBeingStopped, stash + (msg.nodeIdentity.id -> msg)))
}
private def createAndWatchMessageInBoxActor(
childrenBeingStopped: Set[UniqueNodeIdentifier],
stash: Map[UniqueNodeIdentifier, CreateMessageInBoxActor[C]],
msg: CreateMessageInBoxActor[C]
): Unit = {
import msg._
val childRef = childConstructor(userWallet, nodeIdentity)(context)
context.watchWith(childRef, MessageInBoxActorTerminated(nodeIdentity.id))
// In case any of the stop/terminated messages weren't delivered:
context.become(handleChildrenLifecycle(childrenBeingStopped - nodeIdentity.id, stash - nodeIdentity.id))
}
}
object MessageInBoxActors {
trait Command
case class CreateMessageInBoxActor[C <: Currency](userWallet: UnlockedWallet[C], nodeIdentity: NodeIdentity)
extends Command
case class StopMessageInBoxActor(userId: UniqueNodeIdentifier) extends Command
case class MessageInBoxActorTerminated(userId: UniqueNodeIdentifier) extends Command
case class CheckedProps(p: Props, name: String)
def props[C <: Currency](childBuilder: MessageInBoxActorConstructor[C])(implicit
chainId: GlobalChainIdMask
): CheckedProps = CheckedProps(Props(new MessageInBoxActors(childBuilder)), s"MessageInBoxActorGroup_$chainId")
def apply(props: CheckedProps)(implicit context: ActorContext): ActorRef =
context.actorOf(props.p, props.name)
}