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

com.twitter.util.registry.Registry.scala Maven / Gradle / Ivy

There is a newer version: 6.38.0
Show newest version
package com.twitter.util.registry

import com.twitter.util.Local
import scala.annotation.varargs

/**
 * This is an expert-level API; it is not meant for end-users.
 */
final case class Entry(key: Seq[String], value: String)

/**
 * This is an expert-level API; it is not meant for end-users.
 */
object Entry {
  val TupledMethod: ((Seq[String], String)) => Entry = (Entry.apply _).tupled
}

/**
 * This is an expert-level API; it is not meant for end-users.
 *
 * The registry is a hierarchical key/value store, where all keys are sequences
 * of Strings, and values are Strings.
 *
 * Keys and values must be non-control ascii.  If you pass in a key or value
 * with an invalid character, the character will silently be removed.  If this
 * makes your key clash with another key, it will overwrite.
 */
trait Registry extends Iterable[Entry] {
  /**
   * Provides an iterator over the registry.
   *
   * It is the responsibility of the caller to synchronize if they would like to
   * iterate in multiple threads, but the iterator is guaranteed not to change as
   * it is called.
   */
  def iterator: Iterator[Entry]

  /**
   * Registers a  value in the registry, and returns the old value (if any).
   *
   * See `Registry.put(String*)` for the Java usage.
   */
  def put(key: Seq[String], value: String): Option[String]

  /**
   * Registers a value (a non-empty sequence of strings) in the registry, and returns the old
   * value (if any).
   *
   * Note: This is a Java-friendly version of `Registry.put(Seq[String, String])`.
   */
  @varargs
  def put(value: String*): Option[String] = {
    require(value.nonEmpty)
    put(value.init, value.last)
  }

  /**
   * Removes a key from the registry, if it was registered, and returns the old value (if any).
   */
  def remove(key: Seq[String]): Option[String]
}

/**
 * This is an expert-level API; it is not meant for end-users.
 */
class SimpleRegistry extends Registry {
  private[this] var registry = Map.empty[Seq[String], String]

  def iterator: Iterator[Entry] = synchronized(registry).iterator.map(Entry.TupledMethod)

  def put(key: Seq[String], value: String): Option[String] = {
    val sanitizedKey = key.map(sanitize)
    val sanitizedValue = sanitize(value)
    synchronized {
      val result = registry.get(sanitizedKey)
      registry += sanitizedKey -> sanitizedValue
      result
    }
  }

  def remove(key: Seq[String]): Option[String] = {
    val sanitizedKey = key.map(sanitize)
    synchronized {
      val result = registry.get(sanitizedKey)
      registry -= sanitizedKey
      result
    }
  }

  private[this] def sanitize(key: String): String =
    key.filter { char => char > 31 && char < 127 }
}

/**
 * This is an expert-level API; it is not meant for end-users.
 */
object GlobalRegistry {
  private[this] val registry: Registry = new SimpleRegistry

  /**
   * Gets the global registry.
   *
   * If it's call inside of a `withRegistry` context then it's a temporary
   * registry, useful for writing isolated tests.
   */
  def get: Registry = localRegistry() match {
    case None => registry
    case Some(local) => local
  }

  /**
   * Note, this should only ever be updated by methods used for testing.
   */
  private[this] val localRegistry = new Local[Registry]

  /**
   * Changes the global registry to instead return a local one.
   *
   * Takes the registry context with it when moved to a different thread via
   * Twitter concurrency primitives, like `flatMap` on a
   * [[com.twitter.util.Future]].
   */
  def withRegistry[A](replacement: Registry)(fn: => A): A = localRegistry.let(replacement) {
    fn
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy