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

com.box.castle.Castle.scala Maven / Gradle / Ivy

The newest version!
package com.box.castle

import java.net.InetAddress

import akka.actor.ActorSystem
import akka.pattern.gracefulStop
import com.box.castle.committer.api.{CommitterFactory, TopicFilter}
import com.box.castle.consumer.offsetmetadatamanager.ZookeeperOffsetMetadataManagerFactory
import com.box.castle.consumer.{CastleSimpleConsumerFactory, ClientId, ConsumerId}
import com.box.castle.core.{CuratorFactory, util}
import com.box.castle.core.committer.CommitterActorFactory
import com.box.castle.core.committer.manager.CommitterManagerActorFactory
import com.box.castle.core.common.GracefulShutdown
import com.box.castle.core.config.CastleConfig
import com.box.castle.core.leader.{LeaderActorFactory, LeaderFactory, TaskManager}
import com.box.castle.core.supervisor.CastleSupervisorFactory
import com.box.castle.core.worker.{WorkerActorFactory, WorkerFactory}
import com.box.castle.metrics.MetricsLogger
import com.box.castle.router.RouterFactory
import com.box.castle.router.kafkadispatcher.KafkaDispatcherFactory
import com.box.castle.router.proxy.KafkaDispatcherProxyPoolFactory
import org.slf4s.Logging
import java.nio.file.{Path, Paths}

import scala.concurrent.Await

/**
 * This is the entry point class into the Castle framework.  An instance of this class can be used to start and
 * stop the Castle service.
 */
class Castle private (val castleConfig: CastleConfig, val metricsLogger: MetricsLogger, val clientId: ClientId) extends Logging {

  val committerFactoryMap = castleConfig.committerConfigs.map(committerConfig => {
    committerConfig.id -> {
      val clazz = Class.forName(committerConfig.factoryClassName)
      val clazzConstructor =
        try
          clazz.getConstructor(classOf[Map[String, String]])
        catch {
          case _: NoSuchMethodException => throw new NoSuchMethodException(
            s"${committerConfig.factoryClassName} factory must provide a single argument constructor that takes a Map[String,String] type")
        }
      clazzConstructor.newInstance(committerConfig.customConfig).asInstanceOf[CommitterFactory]
    }
  }).toMap

  val topicFilterMap: Map[String, TopicFilter] = committerFactoryMap map {
    idCommitterFactoryPair => {
      idCommitterFactoryPair._1 -> idCommitterFactoryPair._2.createTopicFilter()
    }
  }
  // Let's wire up our dependency tree...
  val curatorFactory = new CuratorFactory(castleConfig)

  val committerActorFactory = new CommitterActorFactory(castleConfig, committerFactoryMap, metricsLogger)

  val useKafkaOffsetMetadataManager = castleConfig.committerConfigs.map(committerConfig =>
    ConsumerId(castleConfig.namespace, committerConfig.id) -> committerConfig.useKafkaMetadataManager
  ).toMap

  val zkConfig = castleConfig.castleZookeeperConfig

  val zookeeperOffsetMetadataManagerFactory =
    new ZookeeperOffsetMetadataManagerFactory(Paths.get(util.join(castleConfig.castleZookeeperConfig.root, castleConfig.namespace)),
      zkConfig.connect, zkConfig.retryPolicy, zkConfig.initialConnectTimeout,
      zkConfig.connectionTimeout, zkConfig.sessionTimeout)

  val boxSimpleConsumerFactory = new CastleSimpleConsumerFactory(clientId,
    Some(zookeeperOffsetMetadataManagerFactory),
    castleConfig.brokerTimeout,
    castleConfig.bufferSizeInBytes,
    useKafkaOffsetMetadataManager=useKafkaOffsetMetadataManager)

  val kafkaDispatcherFactory = new KafkaDispatcherFactory(boxSimpleConsumerFactory, metricsLogger, castleConfig.routerConfig)

  val consumerPoolFactory = new KafkaDispatcherProxyPoolFactory(kafkaDispatcherFactory,
    castleConfig.cacheSizeInBytes,
    metricsLogger)

  val routerActorFactory = new RouterFactory(consumerPoolFactory,
    castleConfig.brokers,
    metricsLogger)

  val fetcherActorFactory = new CommitterManagerActorFactory(committerActorFactory,
    castleConfig.committerConfigs,
    metricsLogger)

  val workerFactory = new WorkerFactory(clientId, curatorFactory)

  val workerActorFactory = new WorkerActorFactory(fetcherActorFactory, workerFactory, castleConfig.committerConfigs, metricsLogger)

  val taskManager = TaskManager(castleConfig.leaderConfig, castleConfig.committerConfigs, topicFilterMap)

  val leaderFactory = new LeaderFactory(castleConfig.leaderConfig,
    clientId,
    curatorFactory,
    taskManager,
    metricsLogger)

  val leaderActorFactory = new LeaderActorFactory(leaderFactory,
    castleConfig.leaderConfig,
    metricsLogger)

  val castleSupervisorFactory = new CastleSupervisorFactory(workerActorFactory,
    leaderActorFactory,
    routerActorFactory,
    committerFactoryMap,
    metricsLogger)

  val system = ActorSystem("castle")

  def start(): Unit = {
    system.actorOf(castleSupervisorFactory.props(), "castle-supervisor")
  }

  /**
   * Stops Castle, this method will block until all committers have shut down gracefully or the grace period
   * specified in the Castle configuration expires.
   */
  def stop(): Unit = {
    val timeout = castleConfig.gracefulShutdownTimeout
    val supervisor = Await.result(system.actorSelection("/user/castle-supervisor").resolveOne(timeout), timeout)
    try {
      Await.result(gracefulStop(supervisor, timeout, GracefulShutdown), timeout)
      log.info("CastleSupervisor has shutdown gracefully, proceeding with shut down of the Akka System")
    } catch {
      case e @ (_: akka.pattern.AskTimeoutException | _: java.util.concurrent.TimeoutException) => {
        log.warn(s"Failed to gracefully shut down CastleSupervisor within the specified grace period of: $timeout")
      }
    }
    system.shutdown()
  }
}

object Castle {
  def apply(castleConfig: CastleConfig, metricsLogger: MetricsLogger = MetricsLogger.defaultLogger, clientId: Option[ClientId] = None): Castle = {
    new Castle(castleConfig,
      metricsLogger, clientId.getOrElse(ClientId(castleConfig.namespace + "-" + InetAddress.getLocalHost.toString)))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy