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

sss.openstar.message.storeservice.MessageQueryHandlerActor.scala Maven / Gradle / Ivy

package sss.openstar.message.storeservice

import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Props}
import akka.pattern.pipe
import sss.ancillary.ByteArrayEncodedStrOps._
import sss.ancillary.Guid
import sss.db.Db
import sss.openstar.balanceledger.TxIndex
import sss.openstar.chains.Chains.GlobalChainIdMask
import sss.openstar.chains.TxWriterActor._
import sss.openstar.controller.Send
import sss.openstar.ledger.{LedgerItem, SeqLedgerItem}
import sss.openstar.message._
import sss.openstar.message.storeservice.MessageQueryHandlerActor.StorageServicePaymentGenerator
import sss.openstar.network.MessageEventBus
import sss.openstar.network.MessageEventBus.IncomingMessage
import sss.openstar.{ExtraMessageKeys, MessageKeys, UniqueNodeIdentifier}

import scala.concurrent.Future
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.util.control.NonFatal
import scala.util.{Failure, Success, Try}

/**
  * Created by alan on 6/8/16.
  */

object MessageQueryHandlerActor {
  type StorageServicePaymentGenerator = Option[TxIndex] => Try[Option[Future[LedgerItem]]]

  def apply(props: Props)(implicit actorSystem: ActorContext): ActorRef = {
    actorSystem.actorOf(props)
  }

  def props(mp: MessagePersist,
            servicePaymentGenerator: StorageServicePaymentGenerator,
            db: Db,
            events: MessageEventBus,
            send: Send,
            globalChainId: GlobalChainIdMask): Props =
    Props(
      classOf[MessageQueryHandlerActor],
      mp,
      servicePaymentGenerator,
      db,
      events,
      send,
      globalChainId
    )

}

private class MessageQueryHandlerActor(messagePersist: MessagePersist,
                               storageServicePaymentGenerator: StorageServicePaymentGenerator)(
  implicit db: Db,
  events: MessageEventBus,
  send: Send,
  chainId: GlobalChainIdMask
)
    extends Actor
    with ActorLogging {

  override def preStart(): Unit = {
    super.preStart()
    events subscribe ExtraMessageKeys.MessageQuery
    events subscribe ExtraMessageKeys.PaywallEnvelope
  }

  log.info("MessageQueryHandler actor has started ...")

  case class MessageTracker(sendingId: UniqueNodeIdentifier,
                            to: String,
                            guid:Guid,
                            resendNetMsg: InternalLedgerItem)

  private var messageSenders: Map[String, MessageTracker] = Map()

  import context.dispatcher

  override def receive: Receive = {

    case mt: MessageTracker =>
      messageSenders += mt.resendNetMsg.le.txId.toBase64Str -> mt
      events publish mt.resendNetMsg

    case IncomingMessage(`chainId`, ExtraMessageKeys.PaywallEnvelope, sendingId, paywallEnvelope: PaywallEnvelope) =>

      Try {
        val txIndex = paywallEnvelope.indexOfPayment
        storageServicePaymentGenerator(txIndex) match {

          case Failure(e) =>
            log.error(e.toString)
            send(ExtraMessageKeys.MessageResponse,
              FailureResponse(
                paywallEnvelope.indexOfPayment.map(_.txId),
                e.toString.take(100)),
              sendingId)
            events publish ToDoBlackList(sendingId, 1.day)

          case Success(Some(ledgerItemF)) =>
            log.info(s"Pending a message ... $paywallEnvelope")
            messagePersist.pending(paywallEnvelope.to,
              paywallEnvelope.msg.guid,
              paywallEnvelope.msg.parentGuid,
              paywallEnvelope.msg.msgPayload
              )

            ledgerItemF map { ledgerItem =>
              val internalLedgerItem = InternalLedgerItem(chainId, SeqLedgerItem(ledgerItem), Some(self))
              MessageTracker(
                sendingId,
                paywallEnvelope.to,
                paywallEnvelope.msg.guid,
                internalLedgerItem)
            } pipeTo self


          case Success(None) => // no paywall payment was required
            messagePersist.pending(paywallEnvelope.to,
              paywallEnvelope.msg.guid,
              paywallEnvelope.msg.parentGuid,
              paywallEnvelope.msg.msgPayload
            )
            messagePersist.accept(paywallEnvelope.to, paywallEnvelope.msg.guid)
            send(
              ExtraMessageKeys.MessageResponse,
              SuccessResponse(txIndex.map(_.txId)), sendingId)
        }

      } recover {
        case e =>
          log.error(e, "Unknown problem accepting incoming message")
          send(ExtraMessageKeys.MessageResponse,
            FailureResponse(
              paywallEnvelope.indexOfPayment.map(_.txId),
              e.getMessage.take(100)),
            sendingId)
      }

    case InternalAck(`chainId`, _) =>
      log.debug("Got ack, waiting for commit or Nack")

    case InternalCommit(`chainId`, blkTxId) =>

      log.debug(s"Got and internal commit for message paywall tx")

      messageSenders.get(blkTxId.blockTxId.txId.toBase64Str) match {
        case Some(tracker) =>
          Try(messagePersist.accept(tracker.to, tracker.guid)) match {
            case Failure(e) =>
              send(ExtraMessageKeys.MessageResponse,
                FailureResponse(
                  blkTxId.blockTxId.txId,
                  e.getMessage.take(100)), tracker.sendingId)
            case Success(_) =>
              log.debug(s"sending ${tracker.sendingId} the Success response")
              send(
                ExtraMessageKeys.MessageResponse,
                SuccessResponse(blkTxId.blockTxId.txId), tracker.sendingId)
          }
          messageSenders -= blkTxId.blockTxId.txId.toBase64Str
        case None =>
          log.error(s"No in memory record of ${blkTxId.blockTxId.txId.toBase64Str}, but it's committed.")
      }

    case InternalTempNack(`chainId`, txMsg) =>

      import context.dispatcher

      messageSenders.get(txMsg.txId.toBase64Str).foreach { tracker =>
        context.system.scheduler
          .scheduleOnce(5 seconds) {
            events.publish(tracker.resendNetMsg)
          }
      }

    case InternalNack(`chainId`, txMsg) =>

      messageSenders.get(txMsg.txId.toBase64Str) foreach { tracker =>
        Try(messagePersist.reject(tracker.to, tracker.guid)) match {
          case Failure(e) =>
            send(
              ExtraMessageKeys.MessageResponse,
              FailureResponse(txMsg.txId, e.getMessage.take(100)),
              tracker.sendingId)
          case Success(_) =>
            send(
              ExtraMessageKeys.MessageResponse,
              FailureResponse(txMsg.txId, txMsg.msg),
              tracker.sendingId)
        }
      }
      messageSenders -= txMsg.txId.toBase64Str



    case IncomingMessage(`chainId`,
                            ExtraMessageKeys.MessageQuery,
                            nId,
                            MessageQuery(who, mostRecentUniqueIncreasing, pageSize, sessKey)) =>


      val page = messagePersist.page(mostRecentUniqueIncreasing, who, pageSize)

      page foreach { m =>
        send(ExtraMessageKeys.PagedMessageMsg,PagedMessage(sessKey, m._2, m._1), nId)
      }

      if (page.size == pageSize) {
        send(ExtraMessageKeys.EndMessagePage, EndMessagePage(sessKey), nId)
      } else {
        send(ExtraMessageKeys.EndMessageQuery, EndMessageQuery(sessKey), nId)
      }

  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy