All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy