All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.rpc.RpcBridgeEventSource.scala Maven / Gradle / Ivy
package sss.openstar.rpc
import akka.NotUsed
import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Cancellable, Props, Status, Terminated}
import akka.stream.scaladsl.{BroadcastHub, Keep, Source}
import akka.stream.{CompletionStrategy, OverflowStrategy}
import com.google.protobuf.ByteString
import sss.db.Db
import sss.openstar.ledger.LedgerId
import sss.openstar.message.MessageInBox
import sss.openstar.message.MessageInBoxActor.{InBoxAdd, InBoxUpdate}
import sss.openstar.network.MessageEventBus
import sss.openstar.rpc.RpcBridgeEventSource.{RpcBridgeEvent, RpcBridgeEventSourceShutdown, SendTimeout}
import sss.openstar.ui.rpc.Event.Event.GenericEvent
import sss.openstar.ui.rpc.EventPayload
import sss.openstar.ui.rpc.{ClientBalance, Event}
import sss.openstar.wallet.ClientWallet.NewClientBalance
import sss.openstar.wallet.UtxoTracker.NewBalance
import sss.openstar.{BusEvent, message}
import scala.concurrent.duration._
import scala.util.Try
object RpcBridgeEventSource {
trait RpcBridgeEvent extends BusEvent {
def eventType: String
def payload: Array[Byte]
}
private case object SendTimeout
case class RpcBridgeEventSourceShutdown(user: String) extends BusEvent
def apply(bridgeUser: String,
ledgerId: LedgerId)(implicit actorContext: ActorContext,
events: MessageEventBus, db :Db
): Source[Event, NotUsed] = {
//Kill old actor
events publish(RpcBridgeEventSourceShutdown(bridgeUser))
implicit val as = actorContext.system
val completionMatcher: PartialFunction[Any, CompletionStrategy] = {
case Status.Success => CompletionStrategy.draining
}
val failureMatcher: PartialFunction[Any, Throwable] = PartialFunction.empty
val sourceActor = Source.actorRef(
completionMatcher,
failureMatcher,
Int.MaxValue,
OverflowStrategy.fail
)
val (sourceRef: ActorRef, outbound: Source[Event, NotUsed]) =
sourceActor
.toMat(BroadcastHub.sink[Event])(Keep.both)
.run()
actorContext.actorOf(
Props(
classOf[RpcBridgeEventSource],
bridgeUser,
ledgerId,
sourceRef,
events,
db)
)
outbound
}
}
private class RpcBridgeEventSource(bridgeUser:String,
ledgerId: LedgerId,
streamRef: ActorRef)
(implicit events: MessageEventBus,
db: Db) extends Actor with ActorLogging {
//Stream will disconnect after 60s idle.
private var timeoutHandle: Option[Cancellable] = None
resetTimeout()
private def resetTimeout() = {
import context.dispatcher
timeoutHandle.map(_.cancel())
timeoutHandle = Some(context.system.scheduler.scheduleOnce(50.seconds, self, SendTimeout))
}
override def preStart(): Unit = {
super.preStart()
events subscribe classOf[NewClientBalance]
events subscribe classOf[NewBalance]
events subscribe classOf[InBoxUpdate]
events subscribe classOf[InBoxAdd]
events subscribe classOf[RpcBridgeEventSourceShutdown]
// add new generic bridge event but keep support for
// previous specific events (above)
events subscribe classOf[RpcBridgeEvent]
}
val inBox = MessageInBox(bridgeUser)
context watch streamRef
log.info(s"EventSource started for $bridgeUser")
override def postStop(): Unit =
timeoutHandle.map(_.cancel())
override def receive: Receive = {
case NewBalance(`ledgerId`, `bridgeUser`, bal) =>
streamRef ! Event(Event.Event.NewBalance(bal.value))
resetTimeout()
case NewClientBalance(`ledgerId`, `bridgeUser`, client, bal, delta) =>
streamRef ! Event(Event.Event.NewClientBalance(ClientBalance(bal.value, client, delta.value)))
resetTimeout()
case InBoxAdd(`bridgeUser`, msg) =>
fireMsgUpdate(msg)
case InBoxUpdate(`bridgeUser`, msg) =>
fireMsgUpdate(msg)
case RpcBridgeEventSourceShutdown(`bridgeUser`) =>
streamRef ! Status.Success
context stop self
case SendTimeout =>
streamRef ! Event(Event.Event.Empty)
resetTimeout()
case e: RpcBridgeEvent =>
streamRef ! Event(GenericEvent(EventPayload(e.eventType, ByteString.copyFrom(e.payload))))
case Terminated(`streamRef`) =>
context stop self
case event =>
//prevent dead letters
log.debug(s"$event")
}
private def fireMsgUpdate(msg: message.Message): Unit = {
Try(inBox.guidToId(msg.guid)) map { id =>
streamRef ! Event(Event.Event.Msg(id))
} recover {
case e => log.error(e, "Got an InBox* for a bad guid?")
}
resetTimeout()
}
}