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

org.elasticmq.server.ElasticMQServer.scala Maven / Gradle / Ivy

package org.elasticmq.server

import org.apache.pekko.actor.{ActorRef, ActorSystem, Props, Terminated}
import org.apache.pekko.http.scaladsl.Http
import org.apache.pekko.util.Timeout
import org.elasticmq.ElasticMQError
import org.elasticmq.actor.QueueManagerActor
import org.elasticmq.actor.queue.QueueEvent
import org.elasticmq.actor.reply._
import org.elasticmq.persistence.file.ConfigBasedQueuePersistenceActor
import org.elasticmq.persistence.sql.SqlQueuePersistenceActor
import org.elasticmq.rest.sqs.{SQSRestServer, TheSQSRestServerBuilder}
import org.elasticmq.rest.stats.{StatisticsRestServer, TheStatisticsRestServerBuilder}
import org.elasticmq.server.config.ElasticMQServerConfig
import org.elasticmq.util.{Logging, NowProvider}

import scala.concurrent.{Await, ExecutionContextExecutor, Future}
import scala.concurrent.duration._
import scala.concurrent.duration.Duration.Inf

class ElasticMQServer(config: ElasticMQServerConfig) extends Logging {
  private val actorSystem = ActorSystem("elasticmq")

  implicit val ec: ExecutionContextExecutor = actorSystem.dispatcher
  implicit val timeout: Timeout = Timeout(5.seconds)

  def start(): () => Terminated = {
    val queueConfigStore: Option[ActorRef] = createQueueEventListener
    val queueManagerActor = createBase(queueConfigStore)
    val restServerOpt = optionallyStartRestSqs(queueManagerActor, queueConfigStore)
    val restStatisticsServerOpt = optionallyStartRestStatistics(queueManagerActor)

    val shutdown = () => {
      val futureTerminationRestSQS = restServerOpt.map(_.stopAndGetFuture()).getOrElse(Future.unit)
      val futureTerminationRestStats = restStatisticsServerOpt.map(_.stopAndGetFuture()).getOrElse(Future.unit)
      val eventualTerminated = for {
        _ <- futureTerminationRestSQS
        _ <- futureTerminationRestStats
        ac <- actorSystem.terminate()
      } yield ac
      Await.result(eventualTerminated, Inf)
    }

    val logErrorsAndShutdown = { (errors: List[ElasticMQError]) =>
      errors.foreach(error => logger.error(s"Could not start server because $error"))
      shutdown()
    }

    queueConfigStore match {
      case Some(queueConfigStoreActor) =>
        restoreQueuesViaQueueEventListener(queueConfigStoreActor, queueManagerActor) map {
          case Some(errors) => logErrorsAndShutdown(errors)
          case None         =>
        }
      case None =>
        createQueuesFromConfig(queueManagerActor) map {
          case Some(errors) => logErrorsAndShutdown(errors)
          case None         =>
        }
    }

    shutdown
  }

  private def createQueueEventListener: Option[ActorRef] =
    if (config.sqlQueuePersistenceConfig.enabled) {
      Some(
        actorSystem.actorOf(Props(new SqlQueuePersistenceActor(config.sqlQueuePersistenceConfig, config.baseQueues)))
      )
    } else if (config.queuesStorageEnabled) {
      Some(
        actorSystem.actorOf(Props(new ConfigBasedQueuePersistenceActor(config.queuesStoragePath, config.baseQueues)))
      )
    } else None

  private def createBase(queueConfigStore: Option[ActorRef]): ActorRef =
    actorSystem.actorOf(Props(new QueueManagerActor(new NowProvider(), config.restSqs.sqsLimits, queueConfigStore)))

  private def optionallyStartRestSqs(
      queueManagerActor: ActorRef,
      queueConfigStore: Option[ActorRef]
  ): Option[SQSRestServer] = {
    if (config.restSqs.enabled) {

      val server = TheSQSRestServerBuilder(
        Some(actorSystem),
        Some(queueManagerActor),
        config.restSqs.bindHostname,
        config.restSqs.bindPort,
        config.nodeAddress,
        config.generateNodeAddress,
        config.restSqs.sqsLimits,
        config.awsRegion,
        config.awsAccountId,
        queueConfigStore
      ).start()

      val _: Http.ServerBinding = server.waitUntilStarted()

      Some(server)
    } else {
      None
    }
  }

  private def optionallyStartRestStatistics(queueManagerActor: ActorRef): Option[StatisticsRestServer] = {
    if (config.restStatisticsConfiguration.enabled) {

      val server = TheStatisticsRestServerBuilder(
        actorSystem,
        queueManagerActor,
        config.restStatisticsConfiguration.bindHostname,
        config.restStatisticsConfiguration.bindPort,
        config.awsRegion,
        config.awsAccountId,
        config.nodeAddress.contextPath
      ).start()

      val _: Http.ServerBinding = server.waitUntilStarted()

      Some(server)
    } else {
      None
    }
  }

  private def restoreQueuesViaQueueEventListener(
      queueEventListenerActor: ActorRef,
      queueManagerActor: ActorRef
  ): Future[Option[List[ElasticMQError]]] =
    (queueEventListenerActor ? QueueEvent.Restore(queueManagerActor)).map(_.swap.toOption)

  private def createQueuesFromConfig(queueManagerActor: ActorRef): Future[Option[List[ElasticMQError]]] = {
    val createQueuesFutures = config.baseQueues.map { createQueue =>
      (queueManagerActor ? org.elasticmq.msg.CreateQueue(createQueue.toCreateQueueData)).map(_.swap.toOption)
    }

    Future.sequence(createQueuesFutures).map { maybeErrors =>
      val errors = maybeErrors.flatten
      if (errors.nonEmpty) Some(errors) else None
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy