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

codacy.events.InitEventQueue.scala Maven / Gradle / Ivy

The newest version!
package codacy.events

import akka.actor.ActorRef
import akka.pattern.{after, ask}
import akka.util.Timeout
import com.newmotion.akka.rabbitmq._
import com.spingo.op_rabbit._
import shapeless.{lazily, Lazy}

import java.util.concurrent.TimeoutException
import scala.collection.JavaConverters._
import scala.concurrent.duration._
import scala.concurrent.Future

final case class RemoveTopics(value: Set[String]) extends AnyVal

final class InitEventQueue(implicit
    cn: Lazy[ComponentName.Components[Future]],
    tp: Lazy[Topic.Components[Future]],
    rm: Lazy[RemoveTopics],
    as: Lazy[AsyncRabbitControl]
) {

  implicit private lazy val to: Timeout = 10.seconds
  private lazy val ctr = lazily[AsyncRabbitControl]
  implicit private def ctx = ctr.actorSystem.dispatcher

  def initEventQueue = {
    for {
      rabbitControl <- ctr.rabbitControl
      topics <- lazily[Topic.Components[Future]].topics
      removeTopics = lazily[RemoveTopics]
      componentName <- lazily[ComponentName.Components[Future]].componentName
      connectionActor <- rabbitControl.ask(RabbitControl.GetConnectionActor).mapTo[ActorRef]
      _ <- waitUntilConnected[ConnectionActor.State](
        connectionActor,
        connected = ConnectionActor.Connected,
        getState = ConnectionActor.GetState
      )
      channelCreated <- connectionActor
        .ask(CreateChannel(ChannelActor.props(), Some("queueInitializer")))
        .mapTo[ChannelCreated]
      _ <- waitUntilConnected[ChannelActor.State](
        channelCreated.channel,
        connected = ChannelActor.Connected,
        getState = ChannelActor.GetState
      )
      message = ChannelMessage(
        _.queueDeclare(
          /*queue=*/ componentName.value, /*durable=*/ true,
          /*exclusive=*/ false, /*autoDelete=*/ false,
          Map.empty[String, Object].asJava
        )
      )
      _ = channelCreated.channel ! message
      _ = topics
        .map(_.routingKey)
        .foreach(key =>
          channelCreated.channel ! ChannelMessage(
            _.queueBind(componentName.value, RabbitControl.topicExchangeName, key)
          )
        )
      _ = removeTopics.value.foreach(key =>
        channelCreated.channel ! ChannelMessage(
          _.queueUnbind(componentName.value, RabbitControl.topicExchangeName, key)
        )
      )
    } yield {
      after(5.minutes, ctr.actorSystem.scheduler)(Future(ctr.actorSystem.stop(channelCreated.channel)))
      ()
    }
  }

  private def waitUntilConnected[State: reflect.ClassTag](
      actor: ActorRef,
      connected: State,
      getState: Any
  ): Future[Unit] = {
    def loop(timeLeft: FiniteDuration): Future[Unit] = {
      if (timeLeft > 0.millis) {
        actor.ask(getState).mapTo[State].flatMap { state =>
          state match {
            case `connected` =>
              Future.unit
            case _ =>
              val timeout = 10.millis
              after(timeout, ctr.actorSystem.scheduler)(loop(timeLeft - timeout))
          }
        }
      } else {
        Future.failed(new TimeoutException(s"$actor didn't connect in $to"))
      }
    }

    loop(to.duration)
  }
}

object InitEventQueue {
  /* for implicit DI */
  implicit def initEventQueueFromRabbitControl(implicit
      rc: Lazy[AsyncRabbitControl],
      cn: Lazy[ComponentName.Components[Future]],
      rm: Lazy[RemoveTopics] = RemoveTopics(Set.empty),
      tp: Lazy[Topic.Components[Future]]
  ) = new InitEventQueue()

  /* for compile-time DI */
  trait Components {
    self: AsyncRabbitControl.Components with ComponentName.Components[Future] with Topic.Components[Future] =>

    def removeTopics: RemoveTopics
    lazy val initEventQueue = initEventQueueFromRabbitControl(asyncRabbitControl, self, removeTopics, self)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy