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.MessageDownloadActor.scala Maven / Gradle / Ivy
package sss.openstar.message
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Cancellable, Props}
import sss.ancillary.{Guid, ShortSessionKey}
import sss.openstar.chains.Chains.GlobalChainIdMask
import sss.openstar.controller.Send
import sss.openstar.identityledger.MessageStoreProviders
import sss.openstar.message.MessageDownloadActor._
import sss.openstar.network.MessageEventBus
import sss.openstar.network.MessageEventBus.IncomingMessage
import sss.openstar.util.ActorTryAndLog
import sss.openstar.{BusEvent, ExtraMessageKeys, MessageKeys, UniqueNodeIdentifier}
import scala.collection.concurrent.TrieMap
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.duration._
/**
* Created by alan on 6/8/16.
*/
object MessageDownloadActor {
case class PublishUnProcessedMessages(forWho: UniqueNodeIdentifier, delayOpt: Option[FiniteDuration] = None) extends BusEvent
case class NewMessageDownloaded(forWho: UniqueNodeIdentifier, msg: Message) extends BusEvent
private case class CheckForMessages(provider: UniqueNodeIdentifier)
case class MessageProcessed(whoProcessed: String, msgGuid: Guid) extends BusEvent
//This is here because I couldn't organise the UI to reliably create a single instance per user
private val onePerNodeId: AtomicNodeActorRefMap = new AtomicReference(Set())
private val refMap: TrieMap[String, ActorRef] = TrieMap()
def apply(
who: UniqueNodeIdentifier,
persistCache: MessageDownloadPersistCache,
sessionKey: ShortSessionKey = ShortSessionKey()
)(implicit actorSystem: ActorContext,
messageEventBus: MessageEventBus,
send: Send,
chainId: GlobalChainIdMask): ActorRef = {
def makeMessageDownloadActor(): ActorRef = actorSystem.actorOf(
Props(classOf[MessageDownloadActor],
onePerNodeId,
who,
persistCache,
sessionKey,
messageEventBus,
send,
chainId), s"MessageDownloadActor_$who"
)
val beforeAdd: Set[UniqueNodeIdentifier] = onePerNodeId.getAndUpdate(all => all + who)
//BUG[psgs-589] TODO FIXME race condition.
if (!beforeAdd(who)) {
refMap.put(who, makeMessageDownloadActor())
}
refMap(who)
}
}
class MessageDownloadActor(
refMap: AtomicNodeActorRefMap,
userId: UniqueNodeIdentifier,
persistCache: MessageDownloadPersistCache,
//userWallet: Wallet,
sessionKey: ShortSessionKey
)(implicit events: MessageEventBus,
send: Send,
chainId: GlobalChainIdMask)
extends Actor
with ActorLogging
with ActorTryAndLog {
override def preStart(): Unit = {
super.preStart()
events subscribe ExtraMessageKeys.PagedMessageMsg
events subscribe ExtraMessageKeys.EndMessageQuery
events subscribe ExtraMessageKeys.EndMessagePage
events subscribe classOf[MessageStoreProviders]
events subscribe classOf[MessageProcessed]
events subscribe classOf[PublishUnProcessedMessages]
}
private var isQuietMap: Map[UniqueNodeIdentifier, Boolean] = Map.empty
private case class MessageTxTracker(msg: Message, provider: String)
//private var tracker: Map[String, MessageTxTracker] = Map()
private var cancellables: Seq[Cancellable] = Seq.empty
private var republishCancellable: Option[Cancellable] = None
log.info("MessageDownload actor ({}) has started...", userId)
override def postStop(): Unit = {
log.info("MessageDownload actor {} down", userId)
cancellables.foreach(_.cancel())
//BUG[psgs-589] TODO FIXME race condition.
refMap.getAndUpdate(refs =>
refs - userId
)
}
private val who = userId
private def createQuery(provider: UniqueNodeIdentifier): Future[MessageQuery] =
persistCache.mostRecentUniqueIncreasing(who, provider)
.map(lastId => {
//log.debug(s"Last id from $provider for $who is $lastId")
MessageQuery(who, lastId, 25, sessionKey)
})
override def receive: Receive = {
case PublishUnProcessedMessages(`who`, None) =>
republishCancellable = None
persistCache.findUnprocessed(who).foreach(msg => events publish NewMessageDownloaded(who, msg))
case PublishUnProcessedMessages(`who`, Some(delay)) if republishCancellable.isEmpty =>
republishCancellable = Some(context.system.scheduler.scheduleOnce(delay, self, PublishUnProcessedMessages(who)))
case PublishUnProcessedMessages(`who`, Some(_)) =>
//ignore as scheduled republish already exists.
case MessageStoreProviders(`who`, providers) => tryAndLog {
val providersPlusSelf = (providers + who).toSeq
isQuietMap = providersPlusSelf.map(_ -> true).toMap
var stagger = 0
cancellables = providersPlusSelf map { provider =>
val cancellable = context.system.scheduler.scheduleAtFixedRate(
FiniteDuration(stagger, TimeUnit.SECONDS),
FiniteDuration(5, TimeUnit.SECONDS),
self,
CheckForMessages(provider))
stagger += 1
cancellable
}
}
case CheckForMessages(provider) => tryAndLog(
isQuietMap.get(provider) foreach {
case true =>
createQuery(provider)
.map { query =>
send(
ExtraMessageKeys.MessageQuery,
query,
provider)
}
case false =>
}
)
case IncomingMessage(_, _, provider, EndMessagePage(`sessionKey`)) =>
self ! CheckForMessages(provider)
isQuietMap += provider -> false
case IncomingMessage(_, _, provider, EndMessageQuery(`sessionKey`)) =>
isQuietMap += provider -> true
case IncomingMessage(_, _, provider, PagedMessage(`sessionKey`, uniqueIncreasing, msg: Message)) => tryAndLog {
msg.parentGuid match {
case None => log.info(s"${msg.guid} no parent")
case Some(p) => log.info(s"${msg.guid} HAS parent $p")
}
isQuietMap += provider -> false
persistCache.saveMessage(
who,
provider,
uniqueIncreasing,
msg
) foreach { futureSuccess =>
log.debug(s"About to publish for $who provider: $provider $uniqueIncreasing")
events publish NewMessageDownloaded(who, msg)
}
}
case MessageProcessed(`who`, guid) =>
persistCache.messageProcessed(who, guid)
case x => //log.debug("Ignored {}", x)
}
}