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

com.twitter.finagle.Announcer.scala Maven / Gradle / Ivy

package com.twitter.finagle

import com.twitter.finagle.util.LoadService
import com.twitter.util.{Closable, Future, Time}
import java.net.{InetSocketAddress, SocketAddress}
import java.util.logging.Logger
import scala.collection.mutable

/**
 * Indicates that an [[com.twitter.finagle.Announcer]] was not found for the
 * given `scheme`.
 *
 * Announcers are discovered via Finagle's [[com.twitter.finagle.util.LoadService]]
 * mechanism. These exceptions typically suggest that there are no libraries
 * on the classpath that define an Announcer for the given scheme.
 */
class AnnouncerNotFoundException(scheme: String)
  extends Exception("Announcer not found for scheme \"%s\"".format(scheme))

/**
 * Indicates that multiple [[com.twitter.finagle.Announcer Announcers]] were
 * discovered for given `scheme`.
 *
 * Announcers are discovered via Finagle's [[com.twitter.finagle.util.LoadService]]
 * mechanism. These exceptions typically suggest that there are multiple
 * libraries on the classpath with conflicting scheme definitions.
 */
class MultipleAnnouncersPerSchemeException(announcers: Map[String, Seq[Announcer]])
  extends NoStacktrace
{
  override def getMessage = {
    val msgs = announcers map { case (scheme, rs) =>
      "%s=(%s)".format(scheme, rs.map(_.getClass.getName).mkString(", "))
    } mkString(" ")
    "Multiple announcers defined: %s".format(msgs)
  }
}

/**
 * Indicates that a forum string passed to an [[com.twitter.finagle.Announcer]]
 * was invalid according to the forum grammar [1].
 *
 * [1] http://twitter.github.io/finagle/guide/Names.html
 */
class AnnouncerForumInvalid(forum: String)
  extends Exception("Announcer forum \"%s\" is not valid".format(forum))

trait Announcement extends Closable {
  def close(deadline: Time) = unannounce()
  def unannounce(): Future[Unit]
}

trait ProxyAnnouncement extends Announcement with Proxy {
  val forums: List[String]
}

trait Announcer {
  val scheme: String
  def announce(addr: InetSocketAddress, name: String): Future[Announcement]
}

object Announcer {
  private[this] lazy val announcers = {
    val announcers = LoadService[Announcer]()
    val log = Logger.getLogger(getClass.getName)

    val dups = announcers groupBy(_.scheme) filter { case (_, rs) => rs.size > 1 }
    if (dups.size > 0) throw new MultipleAnnouncersPerSchemeException(dups)

    for (r <- announcers)
      log.info("Announcer[%s] = %s(%s)".format(r.scheme, r.getClass.getName, r))
    announcers
  }

  def get[T <: Announcer](clazz: Class[T]): Option[T] =
    announcers find { _.getClass isAssignableFrom clazz } map { _.asInstanceOf[T] }

  private[this] val _announcements = mutable.Set[(InetSocketAddress, List[String])]()
  def announcements = synchronized { _announcements.toSet }

  def announce(addr: InetSocketAddress, forum: String): Future[Announcement] = {
    val announcement = forum.split("!", 2) match {
      case Array(scheme, name) =>
        announcers.find(_.scheme == scheme) match {
          case Some(announcer) => announcer.announce(addr, name)
          case None => Future.exception(new AnnouncerNotFoundException(scheme))
        }

      case _ =>
        Future.exception(new AnnouncerForumInvalid(forum))
    }

    announcement map { ann =>
      val lastForums = ann match {
        case a: ProxyAnnouncement => a.forums
        case _ => Nil
      }

      val proxyAnnouncement = new ProxyAnnouncement {
        val self = ann
        def unannounce() = ann.unannounce()
        val forums = forum :: lastForums
      }

      synchronized {
        _announcements -= ((addr, lastForums))
        _announcements += ((addr, proxyAnnouncement.forums))
      }

      proxyAnnouncement
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy