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

com.twitter.finagle.serverset2.Entry.scala Maven / Gradle / Ivy

There is a newer version: 6.37.0
Show newest version
package com.twitter.finagle.serverset2

import collection.JavaConverters._
import collection.mutable.ArrayBuffer

import com.twitter.util.Memoize

/**
 * Represents one logical serverset2 entry.
 */
sealed trait Entry

/**
 * Endpoints encode a destination announced via serversets.
 *
 * @param name The endpoint name. Null describes a default service
 * endpoint.
 *
 * @param host The host of the endpoint (or null if unset).
 *
 * @param port The port of the endpoint (or Int.MinValue if unset).
 *
 * @param shard The shard id of the endpoint (or Int.MinValue if unset).
 *
 * @param status The endpoint's status.
 *
 * @param memberId The endpoint's member id,
 * used as a foreign key for endpoints.
 */
case class Endpoint(
  names: Array[String],
  host: String,
  port: Int,
  shard: Int,
  status: Endpoint.Status.Value,
  memberId: String
) extends Entry {

  override def equals(that: Any) =
    that match {
      case that: Endpoint =>
        java.util.Arrays.equals(
          this.names.asInstanceOf[Array[Object]],
          that.names.asInstanceOf[Array[Object]]) &&
        this.host == that.host &&
        this.port == that.port &&
        this.shard == that.shard &&
        this.status == that.status &&
        this.memberId == that.memberId
      case _ => super.equals(that)
    }
}

object Entry {
  private val EndpointPrefix = "member_"

  /**
   * Parse a JSON response from ZooKeeper into a Seq[Entry].
   */
  def parseJson(path: String, json: String): Seq[Entry] = {
    val basename = path.split("/").last

    if (basename startsWith EndpointPrefix)
      Endpoint.parseJson(json) map(_.copy(memberId=basename))
    else
      Nil
  }
}

object Endpoint {
  val Empty = Endpoint(
    null, null, Int.MinValue,
    Int.MinValue, Endpoint.Status.Unknown, "")

  object Status extends Enumeration {
    val Dead, Starting, Alive, Stopping, Stopped, Warning, Unknown = Value

    private val map = Map(
      "DEAD" -> Dead,
      "STARTING" -> Starting,
      "ALIVE" -> Alive,
      "STOPPING" -> Stopping,
      "STOPPED" -> Stopped,
      "WARNING" -> Warning,
      "UNKNOWN" -> Unknown)

    def ofString(s: String): Option[Value] = map.get(s)
  }

  private def parseEndpoint(m: Any): Option[(String, Int)] =
    m match {
      case ep: java.util.Map[_, _] =>
        val p = Option(ep.get("port")) collect {
          case port: java.lang.Integer => port
        }

        val h = Option(ep.get("host")) collect {
          case host: String => host
        }

        for (h <- h; p <- p)
          yield (h, p.toInt)

      case _ => None
    }

  def parseJson(json: String): Seq[Endpoint] = {
    val d = JsonDict(json)

    val shard = for { IntObj(s) <- d("shard") } yield s
    val status = {
      for {
        StringObj(s) <- d("status")
        status <- Status.ofString(s)
      } yield status
    } getOrElse Endpoint.Status.Unknown
    val tmpl = Endpoint.Empty.copy(shard=shard.getOrElse(Int.MinValue), status=status)

    val namesByHostPort =
      Memoize.snappable[(String, Int), ArrayBuffer[String]] { case (host, port) =>
        new ArrayBuffer[String]
      }
    for (map <- d("serviceEndpoint"); hostport <- parseEndpoint(map))
      namesByHostPort(hostport) += null
    for {
      map <- d("additionalEndpoints") collect {
        case m: java.util.Map[_, _] => m
      }
      key <- map.keySet().asScala collect { case k: String => k }
      if key.isInstanceOf[String]
      hostport <- parseEndpoint(map.get(key))
    } namesByHostPort(hostport) += key

    for (((host, port), names) <- namesByHostPort.snap.toSeq)
    yield tmpl.copy(names=names.toArray, host=host, port=port)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy