codacy.events.EventBus.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of events-rabbitmq_2.13 Show documentation
Show all versions of events-rabbitmq_2.13 Show documentation
A library to send events on rabbit-mq
The newest version!
package codacy.events
import java.nio.charset.Charset
import CirceVersionSpecific._
import akka.actor.{ActorNotFound, ActorRef, InvalidActorNameException, Props}
import com.spingo.op_rabbit._
import shapeless.{lazily, Lazy}
import io.circe.syntax._
import akka.pattern.ask
import codacy.events.internal.{EventActorSystem, EventLogger}
import com.spingo.op_rabbit.Message.Ack
import com.spingo.op_rabbit.properties.DeliveryModePersistence
import io.circe.{parser, Decoder, Json, Printer}
import scala.concurrent.duration._
import scala.concurrent.Future
trait RabbitPickling {
implicit def unmarshallerFromDecoder[A](implicit decoder: Decoder[A]) = {
new RabbitUnmarshaller[A] {
override def unmarshall(value: Array[Byte], contentType: Option[String], contentEncoding: Option[String]) = {
circeJsonMarshaller.unmarshall(value, contentType, contentEncoding).as[A].fold(throw _, identity(_))
}
}
}
implicit lazy val circeJsonMarshaller: RabbitMarshaller[Json] with RabbitUnmarshaller[Json] = {
new RabbitMarshaller[Json] with RabbitUnmarshaller[Json] {
private val encoding = "UTF-8"
private val utf8 = Charset.forName(encoding)
override protected val contentType = "application/json"
override protected val contentEncoding = Some(encoding)
override def marshall(value: Json): Array[Byte] = {
Printer.noSpaces.copy(dropNullValues = true).print(value).getBytes(utf8)
}
override def unmarshall(value: Array[Byte], contentType: Option[String], contentEncoding: Option[String]) = {
val charset = contentEncoding match {
case None | Some(`encoding`) => utf8
case Some(other) => Charset.forName(other)
}
parser.parse(new String(value, charset)).fold(throw _, identity(_))
}
}
}
}
sealed trait PublishedEvent
object PublishedEvent {
private[events] object instance extends PublishedEvent
}
trait EventBus {
def publish[E <: Event](event: E): Future[PublishedEvent]
def terminate(): Future[Unit]
}
object EventBus {
object Empty extends EventBus {
override def publish[E <: Event](event: E) =
Future.successful(PublishedEvent.instance)
override def terminate(): Future[Unit] =
Future.successful(())
}
def empty: EventBus = Empty
trait Components { self: AsyncRabbitControl.Components =>
def eventLogger = internal.defaultEventLogger
lazy val eventBus = {
eventBusFromRabbitControl(self.asyncRabbitControl, eventLogger)
}
}
/*For implicit dependency injection */
implicit def eventBusFromRabbitControl(implicit
rc: Lazy[AsyncRabbitControl],
logger: EventLogger = internal.defaultEventLogger
): EventBus =
new EventBus with RabbitPickling {
private lazy val rabbitControl: AsyncRabbitControl = lazily[AsyncRabbitControl]
implicit private def ectx = rabbitControl.actorSystem.dispatcher
override def publish[E <: Event](event: E) = {
val message =
Message.topic(event.json.asJson, event.pathString, properties = List(DeliveryModePersistence.persistent))
rabbitControl.rabbitControl
.flatMap(_.?(message)(4.seconds))
.collect { case Ack(_) => PublishedEvent.instance }
.recoverWith { case err =>
logger.error(s"Publishing message: ${event.pathString} failed: ${err.getMessage}", err)
Future.failed(err)
}
}
override def terminate(): Future[Unit] = {
Future {
rabbitControl.actorSystem.terminate()
}.map { _ =>
logger.info(s"Terminated actor system successfully.")
}.recoverWith { case err =>
logger.error("Error terminating actor system.", err)
Future.failed(err)
}
}
}
}
trait ConnectionParamsComponents[M[_]] {
def connectionParams: M[ConnectionParams]
}
trait AsyncRabbitControl extends Any {
private[events] def rabbitControl(): Future[ActorRef]
private[events] def actorSystem: EventActorSystem
}
object AsyncRabbitControl {
trait Components { self: ConnectionParamsComponents[Future] =>
def eventActorSystem: EventActorSystem
lazy val asyncRabbitControl =
asyncRabbitControlFromActorSystem(eventActorSystem, self)
}
implicit def asyncRabbitControlFromActorSystem(implicit
as: Lazy[EventActorSystem],
cp: Lazy[ConnectionParamsComponents[Future]]
): AsyncRabbitControl = {
new AsyncRabbitControl {
private lazy val connectionParams = lazily[ConnectionParamsComponents[Future]].connectionParams
override private[events] lazy val actorSystem = lazily[EventActorSystem]
override def rabbitControl() = {
import actorSystem.dispatcher
actorSystem
.actorSelection(actorSystem / ACTOR_NAME)
.resolveOne(500.millis)
.recoverWith { case ActorNotFound(_) =>
connectionParams
.map { params =>
actorSystem.actorOf(Props(new RabbitControl(params)), ACTOR_NAME)
}
.recoverWith { case _: InvalidActorNameException => // concurrent creation handling
rabbitControl()
}
}
}
}
}
private lazy val ACTOR_NAME = "RabbitControl"
}