com.twitter.finagle.Namer.scala Maven / Gradle / Ivy
The newest version!
package com.twitter.finagle
import com.twitter.util.{Var, Try, NonFatal, Memoize, Activity}
import java.net.{InetSocketAddress, SocketAddress}
/**
* A namer is a context in which a [[com.twitter.finagle.NameTree
* NameTree]] is bound. The context is provided by the
* [[com.twitter.finagle.Namer.lookup lookup]] method, which
* translates [[com.twitter.finagle.Path Paths]] into
* [[com.twitter.finagle.NameTree NameTrees]]. Namers may represent
* external processes, for example lookups through DNS or to ZooKeeper,
* and thus lookup results are represented by a [[com.twitter.util.Activity Activity]].
*/
trait Namer { self =>
import NameTree._
/**
* Translate a [[com.twitter.finagle.Path Path]] into a
* [[com.twitter.finagle.NameTree NameTree]].
*/
def lookup(path: Path): Activity[NameTree[Name]]
/**
* Enumerate entries in this namer by prefix.
*/
def enum(prefix: Path): Activity[Dtab]
/**
* Compose this [[com.twitter.finagle.Namer Namer]] with `next`;
* the returned [[com.twitter.finagle.Namer Namer]]. The result is
* the [[com.twitter.finagle.NameTree Alt]]-tree of both lookups,
* with this namer's result first.
*/
def orElse(next: Namer): Namer = Namer.OrElse(self, next)
/**
* Bind the given tree with this namer. Bind recursively follows
* paths by looking them up in this namer. A recursion depth of up
* to 100 is allowed.
*/
def bind(tree: NameTree[Path]): Activity[NameTree[Name.Bound]] =
Namer.bind(this, tree)
/**
* Bind, then evaluate the given NameTree with this Namer. The result
* is translated into a Var[Addr].
*/
def bindAndEval(tree: NameTree[Path]): Var[Addr] =
bind(tree).map(_.eval).run flatMap {
case Activity.Ok(None) => Var.value(Addr.Neg)
case Activity.Ok(Some(names)) => Name.all(names).addr
case Activity.Pending => Var.value(Addr.Pending)
case Activity.Failed(exc) => Var.value(Addr.Failed(exc))
}
/**
* Expand a prefix of this Namer, producing a Dtab reflecting the
* this prefixed namespace. The returned Dtab does not contain any
* root entries (/=>...), and thus represents valid suffixes of the
* given prefix.
*/
final def expand(prefix: Path): Activity[Dtab] = Namer.expand(this, prefix)
}
private case class FailingNamer(exc: Throwable) extends Namer {
def lookup(path: Path): Activity[NameTree[Name]] =
Activity.exception(exc)
def enum(prefix: Path): Activity[Dtab] =
Activity.exception(exc)
}
object Namer {
import NameTree._
/**
* The global [[com.twitter.finagle.Namer Namer]]. It binds paths of the form
*
* {{{
* /$/classname/path...
* }}}
*
* By reflecting in the Java class `classname` whose expected type is a
* [[com.twitter.finagle.Namer Namer]] with a zero-arg constructor,
* and passing the residual path to it. Lookups fail when `classname` does
* not exist or cannot be constructed.
*
* The global namer also handles paths of the form
*
* {{{
* /$/nil/...
* }}}
*
* to force empty resolutions.
*/
val global: Namer = new Namer {
val namerOfKind: (String => Namer) = Memoize {
kind =>
try Class.forName(kind).newInstance().asInstanceOf[Namer] catch {
case NonFatal(exc) => FailingNamer(exc)
}
}
private[this] object InetPath {
def unapply(path: Path): Option[InetSocketAddress] = path match {
case Path.Utf8("$", "inet", IntegerString(port)) =>
Some(new InetSocketAddress(port))
case Path.Utf8("$", "inet", host, IntegerString(port)) =>
Some(new InetSocketAddress(host, port))
case _ => None
}
}
private[this] object FailPath {
val prefix = Path.Utf8("$", "fail")
def unapply(path: Path): Boolean =
path startsWith prefix
}
private[this] object NilPath {
val prefix = Path.Utf8("$", "nil")
def unapply(path: Path): Boolean =
path startsWith prefix
}
private[this] object NamerPath {
def unapply(path: Path): Option[(Namer, Path)] = path match {
case Path.Utf8("$", kind, rest@_*) => Some((namerOfKind(kind), Path.Utf8(rest: _*)))
case _ => None
}
}
def lookup(path: Path): Activity[NameTree[Name]] = path match {
case InetPath(addr) => Activity.value(Leaf(Name.bound(addr)))
case FailPath() => Activity.value(Fail)
case NilPath() => Activity.value(Empty)
case NamerPath(namer, rest) => namer.lookup(rest)
case _ => Activity.value(Neg)
}
def enum(prefix: Path) = prefix match {
case FailPath() => Activity.value(Dtab.fail)
case NilPath() => Activity.value(Dtab.empty)
case NamerPath(namer, rest) => namer.enum(rest)
case _ =>
// Can't enumerate the 'inet' namespace
Activity.exception(new UnsupportedOperationException)
}
override def toString = "Namer.global"
}
private object IntegerString {
def unapply(s: String): Option[Int] =
Try(s.toInt).toOption
}
private object DoubleString {
def unapply(s: String): Option[Double] =
Try(s.toDouble).toOption
}
private val MaxDepth = 100
private def bind(namer: Namer, tree: NameTree[Path]): Activity[NameTree[Name.Bound]] =
bind(namer, 0)(tree map { path => Name.Path(path) })
private def bind(namer: Namer, depth: Int)(tree: NameTree[Name])
: Activity[NameTree[Name.Bound]] =
if (depth > MaxDepth)
Activity.exception(new IllegalArgumentException("Max recursion level reached."))
else tree match {
case Leaf(Name.Path(path)) => namer.lookup(path) flatMap bind(namer, depth+1)
case Leaf([email protected](_)) => Activity.value(Leaf(bound))
case Fail => Activity.value(Fail)
case Neg => Activity.value(Neg)
case Empty => Activity.value(Empty)
case Union() | Alt() => Activity.value(Neg)
case Union(trees@_*) =>
Activity.collect(trees map bind(namer, depth+1)) map Union.fromSeq
case Alt(trees@_*) =>
Activity.collect(trees map bind(namer, depth+1)) map Alt.fromSeq
}
private def expandTree(namer: Namer, depth: Int)(tree: NameTree[Path]): Activity[Dtab] = {
if (depth > MaxDepth)
return Activity.exception(new IllegalArgumentException("Max recursion level reached."))
tree match {
case NameTree.Union(trees@_*) =>
if (trees.isEmpty) Activity.value(Dtab.empty) else {
val expanded = Activity.collect(trees map expandTree(namer, depth+1))
expanded map { dtabs => dtabs.reduceLeft(_.union(_)) }
}
case NameTree.Alt(trees@_*) =>
if (trees.isEmpty) Activity.value(Dtab.empty) else {
val expanded = Activity.collect(trees map expandTree(namer, depth+1))
expanded map { dtabs => dtabs.reduceLeft(_.alt(_)) }
}
case NameTree.Leaf(path) => expandPath(namer, depth+1)(path)
case NameTree.Fail => Activity.value(Dtab(Vector(Dentry(Path(), NameTree.Fail))))
case NameTree.Neg => Activity.value(Dtab(Vector(Dentry(Path(), NameTree.Neg))))
case NameTree.Empty => Activity.value(Dtab.empty)
}
}
private def expandPath(namer: Namer, depth: Int)(prefix: Path): Activity[Dtab] = {
if (depth > MaxDepth)
return Activity.exception(new IllegalArgumentException("Max recursion level reached."))
namer.enum(prefix) flatMap { dtab =>
val dtabs = Activity.collect(dtab map {
case Dentry(Path(), tree) => expandTree(namer, depth+1)(tree)
case dentry => Activity.value(Vector(dentry))
})
dtabs map { dtabs => Dtab(dtabs.flatten) }
}
}
private def expand(namer: Namer, prefix: Path): Activity[Dtab] =
expandPath(namer, 0)(prefix).map(_.simplified)
private case class OrElse(fst: Namer, snd: Namer) extends Namer {
def lookup(path: Path): Activity[NameTree[Name]] =
(fst.lookup(path) join snd.lookup(path)) map {
case (left, right) => NameTree.Alt(left, right)
}
def enum(prefix: Path): Activity[Dtab] =
(fst.enum(prefix) join snd.enum(prefix)) map {
case (left, right) => left.alt(right)
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy