Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
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)
}
}
}