implement SubchannelClassification in MainBus, fixes #1340
This commit is contained in:
parent
70ae4e1984
commit
b2d548bd0e
5 changed files with 261 additions and 15 deletions
|
|
@ -8,6 +8,7 @@ import akka.actor.ActorRef
|
|||
import akka.util.Index
|
||||
import java.util.concurrent.ConcurrentSkipListSet
|
||||
import java.util.Comparator
|
||||
import akka.util.{ Subclassification, SubclassifiedIndex }
|
||||
|
||||
/**
|
||||
* Represents the base type for EventBuses
|
||||
|
|
@ -54,14 +55,14 @@ trait ActorEventBus extends EventBus {
|
|||
/**
|
||||
* Can be mixed into an EventBus to specify that the Classifier type is ActorRef
|
||||
*/
|
||||
trait ActorClassifier { self: EventBus ⇒
|
||||
trait ActorClassifier { this: EventBus ⇒
|
||||
type Classifier = ActorRef
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be mixed into an EventBus to specify that the Classifier type is a Function from Event to Boolean (predicate)
|
||||
*/
|
||||
trait PredicateClassifier { self: EventBus ⇒
|
||||
trait PredicateClassifier { this: EventBus ⇒
|
||||
type Classifier = Event ⇒ Boolean
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +72,7 @@ trait PredicateClassifier { self: EventBus ⇒
|
|||
*
|
||||
* The compareSubscribers need to provide a total ordering of the Subscribers
|
||||
*/
|
||||
trait LookupClassification { self: EventBus ⇒
|
||||
trait LookupClassification { this: EventBus ⇒
|
||||
|
||||
protected final val subscribers = new Index[Classifier, Subscriber](mapSize(), new Comparator[Subscriber] {
|
||||
def compare(a: Subscriber, b: Subscriber): Int = compareSubscribers(a, b)
|
||||
|
|
@ -109,6 +110,70 @@ trait LookupClassification { self: EventBus ⇒
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classification which respects relationships between channels: subscribing
|
||||
* to one channel automatically and idempotently subscribes to all sub-channels.
|
||||
*/
|
||||
trait SubchannelClassification { this: EventBus ⇒
|
||||
|
||||
implicit val subclassification: Subclassification[Classifier]
|
||||
|
||||
// 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]]
|
||||
|
||||
protected def subscribers = cache.values.flatten
|
||||
|
||||
/**
|
||||
* 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)
|
||||
if (diff.isEmpty) false
|
||||
else {
|
||||
cache = cache ++ diff
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
def unsubscribe(subscriber: Subscriber, from: Classifier): Boolean = subscriptions.synchronized {
|
||||
val diff = subscriptions.removeValue(from, subscriber)
|
||||
if (diff.isEmpty) false
|
||||
else {
|
||||
cache = cache ++ diff
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
def unsubscribe(subscriber: Subscriber): Unit = subscriptions.synchronized {
|
||||
val diff = subscriptions.removeValue(subscriber)
|
||||
if (diff.nonEmpty) cache = cache ++ diff
|
||||
}
|
||||
|
||||
def publish(event: Event): Unit = {
|
||||
val c = classify(event)
|
||||
val recv = cache get c getOrElse {
|
||||
subscriptions.synchronized {
|
||||
cache get c getOrElse {
|
||||
val diff = subscriptions.addKey(c)
|
||||
cache = cache ++ diff
|
||||
cache(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
recv foreach (publish(event, _))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Classifiers to Subscribers and selects which Subscriber should receive which publication through scanning through all Subscribers
|
||||
* through the matches(classifier, event) method
|
||||
|
|
@ -169,7 +234,7 @@ trait ScanningClassification { self: EventBus ⇒
|
|||
/**
|
||||
* Maps ActorRefs to ActorRefs to form an EventBus where ActorRefs can listen to other ActorRefs
|
||||
*/
|
||||
trait ActorClassification { self: ActorEventBus with ActorClassifier ⇒
|
||||
trait ActorClassification { this: ActorEventBus with ActorClassifier ⇒
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import scala.annotation.tailrec
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue