akka.dispatch.Dispatchers.scala Maven / Gradle / Ivy
The newest version!
/**
* Copyright (C) 2009-2014 Typesafe Inc.
*/
package akka.dispatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.{ ConcurrentHashMap, ThreadFactory }
import com.typesafe.config.{ ConfigFactory, Config }
import akka.actor.{ Scheduler, DynamicAccess, ActorSystem }
import com.typesafe.config.Config
import akka.actor.{ Scheduler, ActorSystem }
import akka.event.Logging.Warning
import akka.event.EventStream
import scala.concurrent.duration.Duration
import akka.ConfigurationException
import akka.actor.Deploy
/** @note IMPLEMENT IN SCALA.JS
import akka.util.Helpers.ConfigOps
*/
import scala.concurrent.ExecutionContext
/**
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher
*/
trait DispatcherPrerequisites {
def threadFactory: ThreadFactory
def eventStream: EventStream
def scheduler: Scheduler
def dynamicAccess: DynamicAccess
def settings: ActorSystem.Settings
def mailboxes: Mailboxes
def defaultExecutionContext: Option[ExecutionContext]
}
/**
* INTERNAL API
*/
private[akka] final case class DefaultDispatcherPrerequisites(
val threadFactory: ThreadFactory,
val eventStream: EventStream,
val scheduler: Scheduler,
val dynamicAccess: DynamicAccess,
val settings: ActorSystem.Settings,
val mailboxes: Mailboxes,
val defaultExecutionContext: Option[ExecutionContext]) extends DispatcherPrerequisites
object Dispatchers {
/**
* The id of the default dispatcher, also the full key of the
* configuration of the default dispatcher.
*/
final val DefaultDispatcherId = "akka.actor.default-dispatcher"
}
/**
* Dispatchers are to be defined in configuration to allow for tuning
* for different environments. Use the `lookup` method to create
* a dispatcher as specified in configuration.
*
* Look in `akka.actor.default-dispatcher` section of the reference.conf
* for documentation of dispatcher options.
*/
class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: DispatcherPrerequisites) {
import Dispatchers._
/** @note IMPLEMENT IN SCALA.JS
val cachingConfig = new CachingConfig(settings.config)
val defaultDispatcherConfig: Config =
idConfig(DefaultDispatcherId).withFallback(settings.config.getConfig(DefaultDispatcherId))
*/
/**
* The one and only default dispatcher.
*/
def defaultGlobalDispatcher: MessageDispatcher = lookup(DefaultDispatcherId)
private val dispatcherConfigurators = new ConcurrentHashMap[String, MessageDispatcherConfigurator]
/**
* Returns a dispatcher as specified in configuration. Please note that this
* method _may_ create and return a NEW dispatcher, _every_ call.
*
* @throws ConfigurationException if the specified dispatcher cannot be found in the configuration
*/
def lookup(id: String): MessageDispatcher = new DispatcherConfigurator(new Config, prerequisites).dispatcher() /** lookupConfigurator(id).dispatcher() */
/**
* Checks that the configuration provides a section for the given dispatcher.
* This does not guarantee that no ConfigurationException will be thrown when
* using this dispatcher, because the details can only be checked by trying
* to instantiate it, which might be undesirable when just checking.
*/
def hasDispatcher(id: String): Boolean = dispatcherConfigurators.containsKey(id) /** @note IMPLEMENT IN SCALA.JS || cachingConfig.hasPath(id) */
/**@note IMPLEMENT IN SCALA.JS
private def lookupConfigurator(id: String): MessageDispatcherConfigurator = {
dispatcherConfigurators.get(id) match {
/** @note IMPLEMENT IN SCALA.JS
case null ⇒
// It doesn't matter if we create a dispatcher configurator that isn't used due to concurrent lookup.
// That shouldn't happen often and in case it does the actual ExecutorService isn't
// created until used, i.e. cheap.
val newConfigurator =
if (cachingConfig.hasPath(id)) configuratorFrom(config(id))
else throw new ConfigurationException(s"Dispatcher [$id] not configured")
dispatcherConfigurators.putIfAbsent(id, newConfigurator) match {
case null ⇒ newConfigurator
case existing ⇒ existing
}
*/
case existing ⇒ existing
}
}
/**
* Register a [[MessageDispatcherConfigurator]] that will be
* used by [[#lookup]] and [[#hasDispatcher]] instead of looking
* up the configurator from the system configuration.
* This enables dynamic addition of dispatchers, as used by the
* [[akka.routing.BalancingPool]].
*
* A configurator for a certain id can only be registered once, i.e.
* it can not be replaced. It is safe to call this method multiple times,
* but only the first registration will be used. This method returns `true` if
* the specified configurator was successfully registered.
*/
def registerConfigurator(id: String, configurator: MessageDispatcherConfigurator): Boolean =
dispatcherConfigurators.putIfAbsent(id, configurator) == null
*/
/**
* INTERNAL API
*/
/** @note IMPLEMENT IN SCALA.JS
private[akka] def config(id: String): Config = {
config(id, settings.config.getConfig(id))
}
/**
* INTERNAL API
*/
private[akka] def config(id: String, appConfig: Config): Config = {
import scala.collection.JavaConverters._
def simpleName = id.substring(id.lastIndexOf('.') + 1)
idConfig(id)
.withFallback(appConfig)
.withFallback(ConfigFactory.parseMap(Map("name" -> simpleName).asJava))
.withFallback(defaultDispatcherConfig)
}
private def idConfig(id: String): Config = {
import scala.collection.JavaConverters._
ConfigFactory.parseMap(Map("id" -> id).asJava)
}
*/
/**
* INTERNAL API
*
* Creates a dispatcher from a Config. Internal test purpose only.
*
* ex: from(config.getConfig(id))
*
* The Config must also contain a `id` property, which is the identifier of the dispatcher.
*
* Throws: IllegalArgumentException if the value of "type" is not valid
* IllegalArgumentException if it cannot create the MessageDispatcherConfigurator
*/
private[akka] def from(cfg: Config): MessageDispatcher = configuratorFrom(cfg).dispatcher()
/**
* INTERNAL API
*
* Creates a MessageDispatcherConfigurator from a Config.
*
* The Config must also contain a `id` property, which is the identifier of the dispatcher.
*
* Throws: IllegalArgumentException if the value of "type" is not valid
* IllegalArgumentException if it cannot create the MessageDispatcherConfigurator
*/
private def configuratorFrom(cfg: Config): MessageDispatcherConfigurator = {
/** IMPLEMENT IN SCALA.JS
if (!cfg.hasPath("id")) throw new ConfigurationException("Missing dispatcher 'id' property in config: " + cfg.root.render)
cfg.getString("type") match {
case "Dispatcher" ⇒ new DispatcherConfigurator(cfg, prerequisites)
case "BalancingDispatcher" ⇒
// FIXME remove this case in 2.4
throw new IllegalArgumentException("BalancingDispatcher is deprecated, use a BalancingPool instead. " +
"During a migration period you can still use BalancingDispatcher by specifying the full class name: " +
classOf[BalancingDispatcherConfigurator].getName)
case "PinnedDispatcher" ⇒ new PinnedDispatcherConfigurator(cfg, prerequisites)
case fqn ⇒
val args = List(classOf[Config] -> cfg, classOf[DispatcherPrerequisites] -> prerequisites)
prerequisites.dynamicAccess.createInstanceFor[MessageDispatcherConfigurator](fqn, args).recover({
case exception ⇒
throw new ConfigurationException(
("Cannot instantiate MessageDispatcherConfigurator type [%s], defined in [%s], " +
"make sure it has constructor with [com.typesafe.config.Config] and " +
"[akka.dispatch.DispatcherPrerequisites] parameters")
.format(fqn, cfg.getString("id")), exception)
}).get
}
*/
// WE ONLY HAVE ONE DISPATCHER, I'M AFRAID
new DispatcherConfigurator(cfg, prerequisites)
}
}
/**
* Configurator for creating [[akka.dispatch.Dispatcher]].
* Returns the same dispatcher instance for for each invocation
* of the `dispatcher()` method.
*/
class DispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
extends MessageDispatcherConfigurator(config, prerequisites) {
private val instance = new Dispatcher(
this,
/** @note IMPLEMENT IN SCALA.JS config.getString("id"), */ scala.util.Random.nextString(4),
/** @note IMPLEMENT IN SCALA.JS config.getInt("throughput"), */ 10, // TODO: MAKE IT CONFIGURABLE
/** @note IMPLEMENT IN SCALA.JS config.getNanosDuration("throughput-deadline-time"), */ Duration.fromNanos(1000),
configureExecutor(),
/** @note IMPLEMENT IN SCALA.JS config.getMillisDuration("shutdown-timeout")) */ Duration.create(1, TimeUnit.SECONDS))
/**
* Returns the same dispatcher instance for each invocation
*/
override def dispatcher(): MessageDispatcher = instance
}
/**
* INTERNAL API
*/
/** @note IMPLEMENT IN SCALA.J
private[akka] object BalancingDispatcherConfigurator {
private val defaultRequirement =
ConfigFactory.parseString("mailbox-requirement = akka.dispatch.MultipleConsumerSemantics")
def amendConfig(config: Config): Config =
if (config.getString("mailbox-requirement") != Mailboxes.NoMailboxRequirement) config
else defaultRequirement.withFallback(config)
}
/**
* Configurator for creating [[akka.dispatch.BalancingDispatcher]].
* Returns the same dispatcher instance for for each invocation
* of the `dispatcher()` method.
*/
class BalancingDispatcherConfigurator(_config: Config, _prerequisites: DispatcherPrerequisites)
extends MessageDispatcherConfigurator(BalancingDispatcherConfigurator.amendConfig(_config), _prerequisites) {
private val instance = {
val mailboxes = prerequisites.mailboxes
val id = config.getString("id")
val requirement = mailboxes.getMailboxRequirement(config)
if (!classOf[MultipleConsumerSemantics].isAssignableFrom(requirement))
throw new IllegalArgumentException(
"BalancingDispatcher must have 'mailbox-requirement' which implements akka.dispatch.MultipleConsumerSemantics; " +
s"dispatcher [$id] has [$requirement]")
val mailboxType =
if (config.hasPath("mailbox-type")) {
val mt = mailboxes.lookup(id)
if (!requirement.isAssignableFrom(mailboxes.getProducedMessageQueueType(mt)))
throw new IllegalArgumentException(
s"BalancingDispatcher [$id] has 'mailbox-type' [${mt.getClass}] which is incompatible with 'mailbox-requirement' [$requirement]")
mt
} else mailboxes.lookupByQueueType(requirement)
create(mailboxType)
}
protected def create(mailboxType: MailboxType): BalancingDispatcher =
new BalancingDispatcher(
this,
config.getString("id"),
config.getInt("throughput"),
config.getNanosDuration("throughput-deadline-time"),
mailboxType,
configureExecutor(),
config.getMillisDuration("shutdown-timeout"),
config.getBoolean("attempt-teamwork"))
/**
* Returns the same dispatcher instance for each invocation
*/
override def dispatcher(): MessageDispatcher = instance
}
/**
* Configurator for creating [[akka.dispatch.PinnedDispatcher]].
* Returns new dispatcher instance for for each invocation
* of the `dispatcher()` method.
*/
class PinnedDispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
extends MessageDispatcherConfigurator(config, prerequisites) {
private val threadPoolConfig: ThreadPoolConfig = configureExecutor() match {
case e: ThreadPoolExecutorConfigurator ⇒ e.threadPoolConfig
case other ⇒
prerequisites.eventStream.publish(
Warning("PinnedDispatcherConfigurator",
this.getClass,
"PinnedDispatcher [%s] not configured to use ThreadPoolExecutor, falling back to default config.".format(
config.getString("id"))))
ThreadPoolConfig()
}
/**
* Creates new dispatcher for each invocation.
*/
override def dispatcher(): MessageDispatcher =
new PinnedDispatcher(
this, null, config.getString("id"),
config.getMillisDuration("shutdown-timeout"), threadPoolConfig)
}
*/