2018-10-29 17:19:37 +08:00
|
|
|
/*
|
2021-01-08 17:55:38 +01:00
|
|
|
* Copyright (C) 2009-2021 Lightbend Inc. <https://www.lightbend.com>
|
2011-10-11 17:41:25 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka.event
|
|
|
|
|
|
|
|
|
|
import java.util.Comparator
|
2020-04-27 20:32:18 +08:00
|
|
|
import java.util.concurrent.ConcurrentSkipListSet
|
|
|
|
|
import java.util.concurrent.atomic.AtomicReference
|
|
|
|
|
|
2012-10-30 15:08:41 +01:00
|
|
|
import scala.collection.immutable
|
2020-04-27 20:32:18 +08:00
|
|
|
|
2021-02-10 13:20:29 +01:00
|
|
|
import akka.actor.{ ActorRef, ActorSystem }
|
2020-04-27 20:32:18 +08:00
|
|
|
import akka.util.{ Subclassification, SubclassifiedIndex }
|
|
|
|
|
import akka.util.Index
|
2011-10-11 17:41:25 +02:00
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Represents the base type for EventBuses
|
|
|
|
|
* Internally has an Event type, a Classifier type and a Subscriber type
|
|
|
|
|
*
|
2013-03-07 09:05:55 +01:00
|
|
|
* For the Java API, see akka.event.japi.*
|
2011-10-12 14:07:49 +02:00
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
trait EventBus {
|
|
|
|
|
type Event
|
|
|
|
|
type Classifier
|
|
|
|
|
type Subscriber
|
|
|
|
|
|
2014-02-06 15:08:51 +01:00
|
|
|
//#event-bus-api
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Attempts to register the subscriber to the specified Classifier
|
2014-02-06 15:08:51 +01:00
|
|
|
* @return true if successful and false if not (because it was already
|
|
|
|
|
* subscribed to that Classifier, or otherwise)
|
2011-10-12 14:07:49 +02:00
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
def subscribe(subscriber: Subscriber, to: Classifier): Boolean
|
2011-10-12 14:07:49 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Attempts to deregister the subscriber from the specified Classifier
|
2014-02-06 15:08:51 +01:00
|
|
|
* @return true if successful and false if not (because it wasn't subscribed
|
|
|
|
|
* to that Classifier, or otherwise)
|
2011-10-12 14:07:49 +02:00
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
def unsubscribe(subscriber: Subscriber, from: Classifier): Boolean
|
2011-10-12 14:07:49 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Attempts to deregister the subscriber from all Classifiers it may be subscribed to
|
|
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
def unsubscribe(subscriber: Subscriber): Unit
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Publishes the specified Event to this bus
|
|
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
def publish(event: Event): Unit
|
2014-02-06 15:08:51 +01:00
|
|
|
//#event-bus-api
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Represents an EventBus where the Subscriber type is ActorRef
|
|
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
trait ActorEventBus extends EventBus {
|
|
|
|
|
type Subscriber = ActorRef
|
2019-03-11 10:38:24 +01:00
|
|
|
protected def compareSubscribers(a: ActorRef, b: ActorRef) = a.compareTo(b)
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Can be mixed into an EventBus to specify that the Classifier type is ActorRef
|
|
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
trait ActorClassifier { this: EventBus =>
|
2011-10-11 17:41:25 +02:00
|
|
|
type Classifier = ActorRef
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Can be mixed into an EventBus to specify that the Classifier type is a Function from Event to Boolean (predicate)
|
|
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
trait PredicateClassifier { this: EventBus =>
|
|
|
|
|
type Classifier = Event => Boolean
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Maps Subscribers to Classifiers using equality on Classifier to store a Set of Subscribers (hence the need for compareSubscribers)
|
|
|
|
|
* Maps Events to Classifiers through the classify-method (so it knows who to publish to)
|
|
|
|
|
*
|
|
|
|
|
* The compareSubscribers need to provide a total ordering of the Subscribers
|
|
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
trait LookupClassification { this: EventBus =>
|
2011-10-12 11:46:49 +02:00
|
|
|
|
|
|
|
|
protected final val subscribers = new Index[Classifier, Subscriber](mapSize(), new Comparator[Subscriber] {
|
|
|
|
|
def compare(a: Subscriber, b: Subscriber): Int = compareSubscribers(a, b)
|
|
|
|
|
})
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* This is a size hint for the number of Classifiers you expect to have (use powers of 2)
|
|
|
|
|
*/
|
2011-10-12 11:46:49 +02:00
|
|
|
protected def mapSize(): Int
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Provides a total ordering of Subscribers (think java.util.Comparator.compare)
|
|
|
|
|
*/
|
2011-10-12 11:46:49 +02:00
|
|
|
protected def compareSubscribers(a: Subscriber, b: Subscriber): Int
|
2011-10-11 17:41:25 +02:00
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Returns the Classifier associated with the given Event
|
|
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
protected def classify(event: Event): Classifier
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Publishes the given Event to the given Subscriber
|
|
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
protected def publish(event: Event, subscriber: Subscriber): Unit
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
def subscribe(subscriber: Subscriber, to: Classifier): Boolean = subscribers.put(to, subscriber)
|
|
|
|
|
|
|
|
|
|
def unsubscribe(subscriber: Subscriber, from: Classifier): Boolean = subscribers.remove(from, subscriber)
|
|
|
|
|
|
|
|
|
|
def unsubscribe(subscriber: Subscriber): Unit = subscribers.removeValue(subscriber)
|
|
|
|
|
|
2011-10-12 11:46:49 +02:00
|
|
|
def publish(event: Event): Unit = {
|
|
|
|
|
val i = subscribers.valueIterator(classify(event))
|
|
|
|
|
while (i.hasNext) publish(event, i.next())
|
|
|
|
|
}
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
|
2011-11-10 00:26:53 +01:00
|
|
|
/**
|
|
|
|
|
* Classification which respects relationships between channels: subscribing
|
|
|
|
|
* to one channel automatically and idempotently subscribes to all sub-channels.
|
|
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
trait SubchannelClassification { this: EventBus =>
|
2011-11-10 00:26:53 +01:00
|
|
|
|
2014-02-06 15:08:51 +01:00
|
|
|
/**
|
|
|
|
|
* The logic to form sub-class hierarchy
|
|
|
|
|
*/
|
2011-12-29 13:50:54 +01:00
|
|
|
protected implicit def subclassification: Subclassification[Classifier]
|
2011-11-10 00:26:53 +01:00
|
|
|
|
|
|
|
|
// must be lazy to avoid initialization order problem with subclassification
|
|
|
|
|
private lazy val subscriptions = new SubclassifiedIndex[Classifier, Subscriber]()
|
|
|
|
|
|
|
|
|
|
@volatile
|
|
|
|
|
private var cache = Map.empty[Classifier, Set[Subscriber]]
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the Classifier associated with the given Event
|
|
|
|
|
*/
|
|
|
|
|
protected def classify(event: Event): Classifier
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Publishes the given Event to the given Subscriber
|
|
|
|
|
*/
|
|
|
|
|
protected def publish(event: Event, subscriber: Subscriber): Unit
|
|
|
|
|
|
|
|
|
|
def subscribe(subscriber: Subscriber, to: Classifier): Boolean = subscriptions.synchronized {
|
|
|
|
|
val diff = subscriptions.addValue(to, subscriber)
|
2012-09-27 16:51:00 +02:00
|
|
|
addToCache(diff)
|
|
|
|
|
diff.nonEmpty
|
2011-11-10 00:26:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def unsubscribe(subscriber: Subscriber, from: Classifier): Boolean = subscriptions.synchronized {
|
|
|
|
|
val diff = subscriptions.removeValue(from, subscriber)
|
2012-09-28 15:18:05 +02:00
|
|
|
// removeValue(K, V) does not return the diff to remove from or add to the cache
|
|
|
|
|
// but instead the whole set of keys and values that should be updated in the cache
|
|
|
|
|
cache ++= diff
|
2012-09-27 16:51:00 +02:00
|
|
|
diff.nonEmpty
|
2011-11-10 00:26:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def unsubscribe(subscriber: Subscriber): Unit = subscriptions.synchronized {
|
2012-09-27 16:51:00 +02:00
|
|
|
removeFromCache(subscriptions.removeValue(subscriber))
|
2011-11-10 00:26:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def publish(event: Event): Unit = {
|
|
|
|
|
val c = classify(event)
|
2011-11-10 11:34:33 +01:00
|
|
|
val recv =
|
|
|
|
|
if (cache contains c) cache(c) // c will never be removed from cache
|
2019-03-11 10:38:24 +01:00
|
|
|
else
|
|
|
|
|
subscriptions.synchronized {
|
|
|
|
|
if (cache contains c) cache(c)
|
|
|
|
|
else {
|
|
|
|
|
addToCache(subscriptions.addKey(c))
|
|
|
|
|
cache(c)
|
|
|
|
|
}
|
2011-11-10 00:26:53 +01:00
|
|
|
}
|
2019-03-11 10:38:24 +01:00
|
|
|
recv.foreach(publish(event, _))
|
2011-11-10 00:26:53 +01:00
|
|
|
}
|
2012-09-27 13:15:31 +02:00
|
|
|
|
2014-02-13 22:52:01 +00:00
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
* Expensive call! Avoid calling directly from event bus subscribe / unsubscribe.
|
|
|
|
|
*/
|
|
|
|
|
private[akka] def hasSubscriptions(subscriber: Subscriber): Boolean =
|
2015-04-30 09:23:18 +02:00
|
|
|
// FIXME binary incompatible, but I think it is safe to filter out this problem,
|
|
|
|
|
// since it is only called from new functionality in EventStreamUnsubscriber
|
2019-03-11 10:38:24 +01:00
|
|
|
cache.values.exists { _ contains subscriber }
|
2014-02-13 22:52:01 +00:00
|
|
|
|
2012-10-30 15:08:41 +01:00
|
|
|
private def removeFromCache(changes: immutable.Seq[(Classifier, Set[Subscriber])]): Unit =
|
2018-11-21 12:06:29 +01:00
|
|
|
cache = changes.foldLeft(cache) {
|
2019-03-11 10:38:24 +01:00
|
|
|
case (m, (c, cs)) => m.updated(c, m.getOrElse(c, Set.empty[Subscriber]).diff(cs))
|
2012-09-27 13:15:31 +02:00
|
|
|
}
|
|
|
|
|
|
2012-10-30 15:08:41 +01:00
|
|
|
private def addToCache(changes: immutable.Seq[(Classifier, Set[Subscriber])]): Unit =
|
2018-11-21 12:06:29 +01:00
|
|
|
cache = changes.foldLeft(cache) {
|
2019-03-11 10:38:24 +01:00
|
|
|
case (m, (c, cs)) => m.updated(c, m.getOrElse(c, Set.empty[Subscriber]).union(cs))
|
2012-09-27 13:15:31 +02:00
|
|
|
}
|
2014-02-13 22:52:01 +00:00
|
|
|
|
2011-11-10 00:26:53 +01:00
|
|
|
}
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Maps Classifiers to Subscribers and selects which Subscriber should receive which publication through scanning through all Subscribers
|
|
|
|
|
* through the matches(classifier, event) method
|
|
|
|
|
*
|
|
|
|
|
* Note: the compareClassifiers and compareSubscribers must together form an absolute ordering (think java.util.Comparator.compare)
|
|
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
trait ScanningClassification { self: EventBus =>
|
2019-03-11 10:38:24 +01:00
|
|
|
protected final val subscribers =
|
|
|
|
|
new ConcurrentSkipListSet[(Classifier, Subscriber)](new Comparator[(Classifier, Subscriber)] {
|
|
|
|
|
def compare(a: (Classifier, Subscriber), b: (Classifier, Subscriber)): Int =
|
|
|
|
|
compareClassifiers(a._1, b._1) match {
|
|
|
|
|
case 0 => compareSubscribers(a._2, b._2)
|
|
|
|
|
case other => other
|
|
|
|
|
}
|
|
|
|
|
})
|
2011-10-11 17:41:25 +02:00
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Provides a total ordering of Classifiers (think java.util.Comparator.compare)
|
|
|
|
|
*/
|
|
|
|
|
protected def compareClassifiers(a: Classifier, b: Classifier): Int
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Provides a total ordering of Subscribers (think java.util.Comparator.compare)
|
|
|
|
|
*/
|
|
|
|
|
protected def compareSubscribers(a: Subscriber, b: Subscriber): Int
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns whether the specified Classifier matches the specified Event
|
|
|
|
|
*/
|
|
|
|
|
protected def matches(classifier: Classifier, event: Event): Boolean
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Publishes the specified Event to the specified Subscriber
|
|
|
|
|
*/
|
|
|
|
|
protected def publish(event: Event, subscriber: Subscriber): Unit
|
|
|
|
|
|
2011-10-11 17:41:25 +02:00
|
|
|
def subscribe(subscriber: Subscriber, to: Classifier): Boolean = subscribers.add((to, subscriber))
|
2011-10-12 14:07:49 +02:00
|
|
|
|
2011-10-11 17:41:25 +02:00
|
|
|
def unsubscribe(subscriber: Subscriber, from: Classifier): Boolean = subscribers.remove((from, subscriber))
|
2011-10-12 14:07:49 +02:00
|
|
|
|
2011-10-11 17:41:25 +02:00
|
|
|
def unsubscribe(subscriber: Subscriber): Unit = {
|
|
|
|
|
val i = subscribers.iterator()
|
|
|
|
|
while (i.hasNext) {
|
|
|
|
|
val e = i.next()
|
2011-10-12 11:46:49 +02:00
|
|
|
if (compareSubscribers(subscriber, e._2) == 0) i.remove()
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def publish(event: Event): Unit = {
|
|
|
|
|
val currentSubscribers = subscribers.iterator()
|
|
|
|
|
while (currentSubscribers.hasNext) {
|
|
|
|
|
val (classifier, subscriber) = currentSubscribers.next()
|
2011-10-12 11:46:49 +02:00
|
|
|
if (matches(classifier, event))
|
|
|
|
|
publish(event, subscriber)
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
2014-02-22 23:25:54 +00:00
|
|
|
* Maps ActorRefs to ActorRefs to form an EventBus where ActorRefs can listen to other ActorRefs.
|
|
|
|
|
*
|
2016-03-10 10:10:44 +01:00
|
|
|
* All subscribers will be watched by an `akka.event.ActorClassificationUnsubscriber` and unsubscribed when they terminate.
|
2014-02-22 23:25:54 +00:00
|
|
|
* The unsubscriber actor will not be stopped automatically, and if you want to stop using the bus you should stop it yourself.
|
2011-10-12 14:07:49 +02:00
|
|
|
*/
|
2019-02-09 15:25:39 +01:00
|
|
|
trait ManagedActorClassification { this: ActorEventBus with ActorClassifier =>
|
2011-10-11 17:41:25 +02:00
|
|
|
import scala.annotation.tailrec
|
2014-02-22 23:25:54 +00:00
|
|
|
|
|
|
|
|
protected def system: ActorSystem
|
|
|
|
|
|
2019-03-13 10:56:20 +01:00
|
|
|
private class ManagedActorClassificationMappings(
|
|
|
|
|
val seqNr: Int,
|
|
|
|
|
val backing: Map[ActorRef, immutable.TreeSet[ActorRef]]) {
|
2014-02-22 23:25:54 +00:00
|
|
|
|
|
|
|
|
def get(monitored: ActorRef): immutable.TreeSet[ActorRef] = backing.getOrElse(monitored, empty)
|
|
|
|
|
|
|
|
|
|
def add(monitored: ActorRef, monitor: ActorRef) = {
|
|
|
|
|
val watchers = backing.get(monitored).getOrElse(empty) + monitor
|
2015-04-30 09:23:18 +02:00
|
|
|
new ManagedActorClassificationMappings(seqNr + 1, backing.updated(monitored, watchers))
|
2014-02-22 23:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def remove(monitored: ActorRef, monitor: ActorRef) = {
|
|
|
|
|
val monitors = backing.get(monitored).getOrElse(empty) - monitor
|
2015-04-30 09:23:18 +02:00
|
|
|
new ManagedActorClassificationMappings(seqNr + 1, backing.updated(monitored, monitors))
|
2014-02-22 23:25:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def remove(monitored: ActorRef) = {
|
|
|
|
|
val v = backing - monitored
|
2015-04-30 09:23:18 +02:00
|
|
|
new ManagedActorClassificationMappings(seqNr + 1, v)
|
2014-02-22 23:25:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-30 09:23:18 +02:00
|
|
|
private val mappings = new AtomicReference[ManagedActorClassificationMappings](
|
|
|
|
|
new ManagedActorClassificationMappings(0, Map.empty[ActorRef, immutable.TreeSet[ActorRef]]))
|
2014-02-22 23:25:54 +00:00
|
|
|
|
|
|
|
|
private val empty = immutable.TreeSet.empty[ActorRef]
|
|
|
|
|
|
|
|
|
|
/** The unsubscriber takes care of unsubscribing actors, which have terminated. */
|
2021-02-01 15:38:29 +00:00
|
|
|
protected lazy val unsubscriber =
|
|
|
|
|
ActorClassificationUnsubscriber.start(system, this.toString(), (this.unsubscribe: ActorRef => Unit))
|
2011-10-11 17:41:25 +02:00
|
|
|
|
|
|
|
|
@tailrec
|
|
|
|
|
protected final def associate(monitored: ActorRef, monitor: ActorRef): Boolean = {
|
2014-02-22 23:25:54 +00:00
|
|
|
val current = mappings.get
|
|
|
|
|
|
|
|
|
|
current.backing.get(monitored) match {
|
2019-02-09 15:25:39 +01:00
|
|
|
case None =>
|
2014-02-22 23:25:54 +00:00
|
|
|
val added = current.add(monitored, monitor)
|
|
|
|
|
|
|
|
|
|
if (mappings.compareAndSet(current, added)) registerWithUnsubscriber(monitor, added.seqNr)
|
|
|
|
|
else associate(monitored, monitor)
|
|
|
|
|
|
2019-02-09 15:25:39 +01:00
|
|
|
case Some(monitors) =>
|
2014-02-22 23:25:54 +00:00
|
|
|
if (monitors.contains(monitored)) false
|
2011-10-11 17:41:25 +02:00
|
|
|
else {
|
2014-02-22 23:25:54 +00:00
|
|
|
val added = current.add(monitored, monitor)
|
|
|
|
|
val noChange = current.backing == added.backing
|
|
|
|
|
|
|
|
|
|
if (noChange) false
|
|
|
|
|
else if (mappings.compareAndSet(current, added)) registerWithUnsubscriber(monitor, added.seqNr)
|
|
|
|
|
else associate(monitored, monitor)
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-22 23:25:54 +00:00
|
|
|
protected final def dissociate(actor: ActorRef): Unit = {
|
2011-10-11 17:41:25 +02:00
|
|
|
@tailrec
|
2014-02-22 23:25:54 +00:00
|
|
|
def dissociateAsMonitored(monitored: ActorRef): Unit = {
|
|
|
|
|
val current = mappings.get
|
|
|
|
|
if (current.backing.contains(monitored)) {
|
|
|
|
|
val removed = current.remove(monitored)
|
|
|
|
|
if (!mappings.compareAndSet(current, removed))
|
|
|
|
|
dissociateAsMonitored(monitored)
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def dissociateAsMonitor(monitor: ActorRef): Unit = {
|
2014-02-22 23:25:54 +00:00
|
|
|
val current = mappings.get
|
|
|
|
|
val i = current.backing.iterator
|
|
|
|
|
while (i.hasNext) {
|
|
|
|
|
val (key, value) = i.next()
|
|
|
|
|
value match {
|
2019-02-09 15:25:39 +01:00
|
|
|
case null =>
|
2014-02-22 23:25:54 +00:00
|
|
|
// do nothing
|
|
|
|
|
|
2019-02-09 15:25:39 +01:00
|
|
|
case monitors =>
|
2011-10-11 17:41:25 +02:00
|
|
|
if (monitors.contains(monitor))
|
2014-02-22 23:25:54 +00:00
|
|
|
dissociate(key, monitor)
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-11 10:38:24 +01:00
|
|
|
try {
|
|
|
|
|
dissociateAsMonitored(actor)
|
|
|
|
|
} finally {
|
|
|
|
|
dissociateAsMonitor(actor)
|
|
|
|
|
}
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@tailrec
|
|
|
|
|
protected final def dissociate(monitored: ActorRef, monitor: ActorRef): Boolean = {
|
2014-02-22 23:25:54 +00:00
|
|
|
val current = mappings.get
|
|
|
|
|
|
|
|
|
|
current.backing.get(monitored) match {
|
2019-02-09 15:25:39 +01:00
|
|
|
case None => false
|
|
|
|
|
case Some(monitors) =>
|
2014-02-22 23:25:54 +00:00
|
|
|
val removed = current.remove(monitored, monitor)
|
|
|
|
|
val removedMonitors = removed.get(monitored)
|
|
|
|
|
|
|
|
|
|
if (monitors.isEmpty || monitors == removedMonitors) {
|
|
|
|
|
false
|
2011-10-11 17:41:25 +02:00
|
|
|
} else {
|
2014-02-22 23:25:54 +00:00
|
|
|
if (mappings.compareAndSet(current, removed)) unregisterFromUnsubscriber(monitor, removed.seqNr)
|
|
|
|
|
else dissociate(monitored, monitor)
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* Returns the Classifier associated with the specified Event
|
|
|
|
|
*/
|
2011-10-11 17:41:25 +02:00
|
|
|
protected def classify(event: Event): Classifier
|
|
|
|
|
|
2011-10-12 14:07:49 +02:00
|
|
|
/**
|
|
|
|
|
* This is a size hint for the number of Classifiers you expect to have (use powers of 2)
|
|
|
|
|
*/
|
|
|
|
|
protected def mapSize: Int
|
|
|
|
|
|
2014-02-22 23:25:54 +00:00
|
|
|
def publish(event: Event): Unit = {
|
|
|
|
|
mappings.get.backing.get(classify(event)) match {
|
2019-02-09 15:25:39 +01:00
|
|
|
case None => ()
|
|
|
|
|
case Some(refs) => refs.foreach { _ ! event }
|
2014-02-22 23:25:54 +00:00
|
|
|
}
|
2011-10-11 17:41:25 +02:00
|
|
|
}
|
|
|
|
|
|
2012-06-19 11:39:05 +02:00
|
|
|
def subscribe(subscriber: Subscriber, to: Classifier): Boolean =
|
|
|
|
|
if (subscriber eq null) throw new IllegalArgumentException("Subscriber is null")
|
|
|
|
|
else if (to eq null) throw new IllegalArgumentException("Classifier is null")
|
|
|
|
|
else associate(to, subscriber)
|
|
|
|
|
|
|
|
|
|
def unsubscribe(subscriber: Subscriber, from: Classifier): Boolean =
|
|
|
|
|
if (subscriber eq null) throw new IllegalArgumentException("Subscriber is null")
|
|
|
|
|
else if (from eq null) throw new IllegalArgumentException("Classifier is null")
|
|
|
|
|
else dissociate(from, subscriber)
|
|
|
|
|
|
|
|
|
|
def unsubscribe(subscriber: Subscriber): Unit =
|
|
|
|
|
if (subscriber eq null) throw new IllegalArgumentException("Subscriber is null")
|
|
|
|
|
else dissociate(subscriber)
|
2014-02-22 23:25:54 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[akka] def registerWithUnsubscriber(subscriber: ActorRef, seqNr: Int): Boolean = {
|
|
|
|
|
unsubscriber ! ActorClassificationUnsubscriber.Register(subscriber, seqNr)
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* INTERNAL API
|
|
|
|
|
*/
|
|
|
|
|
private[akka] def unregisterFromUnsubscriber(subscriber: ActorRef, seqNr: Int): Boolean = {
|
|
|
|
|
unsubscriber ! ActorClassificationUnsubscriber.Unregister(subscriber, seqNr)
|
|
|
|
|
true
|
|
|
|
|
}
|
2011-10-28 15:55:15 +02:00
|
|
|
}
|