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

akka.messenger.actors.ConnectorActor.scala Maven / Gradle / Ivy

The newest version!
package akka.messenger.actors

import akka.actor.{Actor, Props}
import akka.cluster.pubsub.DistributedPubSubMediator.{Send, Subscribe, Unsubscribe}
import akka.cluster.pubsub.{DistributedPubSub, DistributedPubSubMediator}
import akka.messenger.api.messages.{Command, Event, Query}
import akka.messenger.messages._
import akka.messenger.api.exceptions.RequestTimeout
import java.util.UUID
import scala.collection.mutable
import scala.concurrent.{Future, Promise}

private[messenger] object ConnectorActor {
  def props(serviceName: String): Props = Props {
    new ConnectorActor(serviceName)
  }
}

private final class ConnectorActor(private val thisServiceName: String) extends Actor {

  private case class RestoreState(queryHandler: Option[PartialFunction[Query, Future[Event]]],
                                  commandHandler: Option[PartialFunction[Command, Future[Event]]],
                                  eventHandler: Option[PartialFunction[Event, Unit]],
                                  requests: mutable.HashMap[String, Promise[Event]])

  private case class TimeoutRequestPromise(requestId: String)

  private val mediator = DistributedPubSub(context.system).mediator
  mediator ! DistributedPubSubMediator.Put(self)

  private var queryHandlerFunction: Option[PartialFunction[Query, Future[Event]]] = None
  private var commandHandlerFunction: Option[PartialFunction[Command, Future[Event]]] = None
  private var eventHandlerFunction: Option[PartialFunction[Event, Unit]] = None
  private val requestPromises = new mutable.HashMap[String, Promise[Event]]()

  override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
    super.preRestart(reason, message)
    self ! RestoreState(
      queryHandler = queryHandlerFunction,
      commandHandler = commandHandlerFunction,
      eventHandler = eventHandlerFunction,
      requests = requestPromises)
  }

  override def receive: Receive = {
    case askQuery: AskQuery if askQuery.fromServiceName == thisServiceName =>
      val thisRequestId = UUID.randomUUID().toString
      mediator ! Send(path = s"/user/${akka.messenger.api.systemName}", msg = askQuery.copy(requestId = Some(thisRequestId), fromNodeRef = Some(self)), localAffinity = false)
      val promise = Promise[Event]()
      requestPromises += thisRequestId -> promise
      context.system.scheduler.scheduleOnce(askQuery.timeout) {
        self ! TimeoutRequestPromise(requestId = thisRequestId)
      }(context.system.dispatcher)
      sender() ! promise.future

    case answerQuery: AnswerQuery if answerQuery.fromServiceName == thisServiceName =>
      mediator ! Send(path = s"/user/${akka.messenger.api.systemName}", msg = answerQuery, localAffinity = false)

    case tellCommand: TellCommand if tellCommand.fromServiceName == thisServiceName =>
      val thisRequestId = UUID.randomUUID().toString
      mediator ! Send(path = s"/user/${akka.messenger.api.systemName}", msg = tellCommand.copy(requestId = Some(thisRequestId), fromNodeRef = Some(self)), localAffinity = false)
      val promise = Promise[Event]()
      requestPromises += thisRequestId -> promise
      context.system.scheduler.scheduleOnce(tellCommand.timeout) {
        self ! TimeoutRequestPromise(requestId = thisRequestId)
      }(context.system.dispatcher)
      sender() ! promise.future

    case answerCommand: AnswerCommand if answerCommand.fromServiceName == thisServiceName =>
      mediator ! Send(path = s"/user/${akka.messenger.api.systemName}", msg = answerCommand, localAffinity = false)
      self ! NotifyEvent(requestId = answerCommand.requestId, fromServiceName = answerCommand.fromServiceName, event = answerCommand.event)

    case notifyEvent: NotifyEvent if notifyEvent.fromServiceName == thisServiceName =>
      val thisRequestId = notifyEvent.requestId match {
        case Some(requestId) => requestId
        case None => UUID.randomUUID().toString
      }
      mediator ! Send(path = s"/user/${akka.messenger.api.systemName}", msg = notifyEvent.copy(requestId = Some(thisRequestId)), localAffinity = false)

    case SubscribeToServiceEvents(fromServiceName, group) =>
      mediator ! Subscribe(topic = fromServiceName, group.map(g => s"$thisServiceName-$g"), self)

    case UnsubscribeFromServiceEvents(fromServiceName) =>
      mediator ! Unsubscribe(topic = fromServiceName, self)

    case askQuery: AskQuery if askQuery.toServiceName == thisServiceName =>
      queryHandlerFunction.foreach { handler =>
        handler(askQuery.query).map { event =>
          self ! AnswerQuery(requestId = askQuery.requestId, fromServiceName = thisServiceName, toServiceName = askQuery.fromServiceName, toNodeRef = askQuery.fromNodeRef, event = event)
        }(context.dispatcher)
      }

    case answerQuery: AnswerQuery if answerQuery.toServiceName == thisServiceName =>
      for {
        requestId <- answerQuery.requestId
        promise <- requestPromises.get(requestId)
      } {
        requestPromises -= requestId
        promise.success(answerQuery.event)
      }

    case tellCommand: TellCommand if tellCommand.toServiceName == thisServiceName =>
      commandHandlerFunction.foreach { handler =>
        handler(tellCommand.command).map { event =>
          self ! AnswerCommand(requestId = tellCommand.requestId, fromServiceName = thisServiceName, toServiceName = tellCommand.fromServiceName, toNodeRef = tellCommand.fromNodeRef, event = event)
        }(context.dispatcher)
      }

    case answerCommand: AnswerCommand if answerCommand.toServiceName == thisServiceName =>
      for {
        requestId <- answerCommand.requestId
        promise <- requestPromises.get(requestId)
      } {
        requestPromises -= requestId
        promise.success(answerCommand.event)
      }

    case TimeoutRequestPromise(requestId) =>
      requestPromises.get(requestId).foreach { promise =>
        requestPromises -= requestId
        promise.failure(RequestTimeout(requestId = requestId))
      }

    case notifyEvent: NotifyEvent if notifyEvent.fromServiceName != thisServiceName =>
      eventHandlerFunction.foreach(handler => handler(notifyEvent.event))

    case InstallQueryHandlerFunction(queryHandler) =>
      queryHandlerFunction = Some(queryHandler)

    case InstallCommandHandlerFunction(commandHandler) =>
      commandHandlerFunction = Some(commandHandler)

    case InstallEventHandlerFunction(eventHandler) =>
      eventHandlerFunction = Some(eventHandler)

    case RestoreState(
    queryHandler,
    commandHandler,
    eventHandler,
    requests) =>
      queryHandlerFunction = queryHandler
      commandHandlerFunction = commandHandler
      eventHandlerFunction = eventHandler
      requestPromises ++= requests
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy