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

com.metamx.common.scala.net.curator.Discotheque.scala Maven / Gradle / Ivy

package com.metamx.common.scala.net.curator

import com.metamx.common.ISE
import com.metamx.common.lifecycle.Lifecycle
import com.metamx.common.scala.collection.concurrent.PermanentMap
import org.apache.curator.framework.CuratorFramework
import org.joda.time.Duration

/**
  * Collection of Disco instances to avoid multiple instances creation for the same endpoint (zkConnect, discoPath).
  *
  * If DiscoAnnounceConfig is defined and disco for this endpoint already created for different announce config then
  * [[IllegalStateException]] will be thrown.
  *
  * All methods are thread safe.
 */
class Discotheque(lifecycle: Lifecycle)
{
  private val curators = new PermanentMap[CuratorConfig, CuratorFramework]()
  private val discos = new PermanentMap[DiscoEndpoint, (Disco, Option[DiscoAnnounceConfig])]()

  def disco(config: CuratorConfig with DiscoConfig): Disco = {
    disco(config.zkConnect, config.discoPath, config.discoAnnounce, config.zkTimeout)
  }

  def disco(curatorConfig: CuratorConfig, discoConfig: DiscoConfig): Disco = {
    disco(curatorConfig.zkConnect, discoConfig.discoPath, discoConfig.discoAnnounce, curatorConfig.zkTimeout)
  }

  def disco(
    discoZkConnect: String,
    discoPath: String,
    discoAnnounceConfig: Option[DiscoAnnounceConfig] = None,
    discoZkTimeout: Duration = Duration.parse("PT15S")
  ): Disco = {
    val endpoint = DiscoEndpoint(discoZkConnect, discoPath)

    val (disco, announceConfig) = discos.getOrElseUpdate(endpoint, {
      val curatorConfig = endpoint.toCuratorConfig(discoZkTimeout)
      val curator = curators.getOrElseUpdate(curatorConfig, Curator.create(curatorConfig, lifecycle))

      val discoConfig = endpoint.toDiscoConfig(discoAnnounceConfig)
      (lifecycle.addMaybeStartManagedInstance(new Disco(curator, discoConfig)), discoAnnounceConfig)
    })

    if (discoAnnounceConfig.isDefined && discoAnnounceConfig != announceConfig) {
      throw new ISE("Failed to create announce [%s] for zkConnect[%s] and path[%s]. Already announced to [%s]".format(
        discoAnnounceConfig.toString,
        discoZkConnect,
        discoPath,
        announceConfig.map(_.toString).getOrElse("None")
      ))
    }

    disco
  }

  private case class DiscoEndpoint(discoZkConnect: String, discoPath: String)
  {
    def toCuratorConfig(zookeeperTimeout: Duration): CuratorConfig = new CuratorConfig
    {
      override def zkTimeout: Duration = zookeeperTimeout

      override def zkConnect: String = DiscoEndpoint.this.discoZkConnect
    }

    def toDiscoConfig(discoAnnounceConfig: Option[DiscoAnnounceConfig]) = new DiscoConfig
    {
      def discoAnnounce = discoAnnounceConfig

      def discoPath = DiscoEndpoint.this.discoPath
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy