Moved ConcurrentSet entries/etc into subscription -> BEST performance for subscription iteration w/o GC
This commit is contained in:
parent
618e4034f2
commit
b2cbd9c084
@ -39,12 +39,12 @@ package dorkbox.util.messagebus.subscription;
|
|||||||
|
|
||||||
import com.esotericsoftware.kryo.util.IdentityMap;
|
import com.esotericsoftware.kryo.util.IdentityMap;
|
||||||
import com.esotericsoftware.reflectasm.MethodAccess;
|
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||||
|
import dorkbox.util.messagebus.common.Entry;
|
||||||
import dorkbox.util.messagebus.common.MessageHandler;
|
import dorkbox.util.messagebus.common.MessageHandler;
|
||||||
import dorkbox.util.messagebus.dispatch.IHandlerInvocation;
|
import dorkbox.util.messagebus.dispatch.IHandlerInvocation;
|
||||||
import dorkbox.util.messagebus.dispatch.ReflectiveHandlerInvocation;
|
import dorkbox.util.messagebus.dispatch.ReflectiveHandlerInvocation;
|
||||||
import dorkbox.util.messagebus.dispatch.SynchronizedHandlerInvocation;
|
import dorkbox.util.messagebus.dispatch.SynchronizedHandlerInvocation;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
|
|
||||||
@ -65,8 +65,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
|||||||
*/
|
*/
|
||||||
public final
|
public final
|
||||||
class Subscription {
|
class Subscription {
|
||||||
private static final int GROW_SIZE = 8;
|
|
||||||
|
|
||||||
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
|
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
|
||||||
public final int ID = ID_COUNTER.getAndIncrement();
|
public final int ID = ID_COUNTER.getAndIncrement();
|
||||||
|
|
||||||
@ -78,17 +76,18 @@ class Subscription {
|
|||||||
private final MessageHandler handler;
|
private final MessageHandler handler;
|
||||||
private final IHandlerInvocation invocation;
|
private final IHandlerInvocation invocation;
|
||||||
|
|
||||||
// NOTE: this is still inside the single-writer! can use the same techniques as subscription manager (for thread safe publication)
|
|
||||||
private int firstFreeSpot = 0; // only touched by a single thread
|
|
||||||
private volatile Object[] listeners = new Object[GROW_SIZE]; // only modified by a single thread
|
|
||||||
|
|
||||||
private final IdentityMap<Object, Integer> listenerMap = new IdentityMap<>(GROW_SIZE);
|
|
||||||
|
|
||||||
// Recommended for best performance while adhering to the "single writer principle". Must be static-final
|
// Recommended for best performance while adhering to the "single writer principle". Must be static-final
|
||||||
private static final AtomicReferenceFieldUpdater<Subscription, Object[]> listenersREF =
|
private static final AtomicReferenceFieldUpdater<Subscription, Entry> headREF =
|
||||||
AtomicReferenceFieldUpdater.newUpdater(Subscription.class,
|
AtomicReferenceFieldUpdater.newUpdater(Subscription.class,
|
||||||
Object[].class,
|
Entry.class,
|
||||||
"listeners");
|
"head");
|
||||||
|
|
||||||
|
|
||||||
|
// This is only touched by a single thread!
|
||||||
|
private final IdentityMap<Object, Entry> entries; // maintain a map of entries for FAST lookup during unsubscribe.
|
||||||
|
|
||||||
|
// this is still inside the single-writer, and can use the same techniques as subscription manager (for thread safe publication)
|
||||||
|
public volatile Entry head; // reference to the first element
|
||||||
|
|
||||||
|
|
||||||
public
|
public
|
||||||
@ -102,6 +101,21 @@ class Subscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.invocation = invocation;
|
this.invocation = invocation;
|
||||||
|
|
||||||
|
entries = new IdentityMap<>(32, SubscriptionManager.LOAD_FACTOR);
|
||||||
|
|
||||||
|
|
||||||
|
if (handler.acceptsSubtypes()) {
|
||||||
|
// keep a list of "super-class" messages that access this. This is updated by multiple threads. This is so we know WHAT
|
||||||
|
// super-subscriptions to clear when we sub/unsub
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called on shutdown for GC purposes
|
||||||
|
public void clear() {
|
||||||
|
this.entries.clear();
|
||||||
|
this.head.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// only used in unit tests to verify that the subscription manager is working correctly
|
// only used in unit tests to verify that the subscription manager is working correctly
|
||||||
@ -118,31 +132,14 @@ class Subscription {
|
|||||||
public
|
public
|
||||||
void subscribe(final Object listener) {
|
void subscribe(final Object listener) {
|
||||||
// single writer principle!
|
// single writer principle!
|
||||||
|
Entry head = headREF.get(this);
|
||||||
|
|
||||||
Object[] localListeners = listenersREF.get(this);
|
if (!entries.containsKey(listener)) {
|
||||||
|
head = new Entry(listener, head);
|
||||||
|
|
||||||
final int length = localListeners.length;
|
entries.put(listener, head);
|
||||||
int spotToPlace = firstFreeSpot;
|
headREF.lazySet(this, head);
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (spotToPlace >= length) {
|
|
||||||
// if we couldn't find a place to put the listener, grow the array, but it is never shrunk
|
|
||||||
localListeners = Arrays.copyOf(localListeners, length + GROW_SIZE, Object[].class);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localListeners[spotToPlace] == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
spotToPlace++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listenerMap.put(listener, spotToPlace);
|
|
||||||
localListeners[spotToPlace] = listener;
|
|
||||||
|
|
||||||
// mark this spot as taken, so the next subscribe starts out a little ahead
|
|
||||||
firstFreeSpot = spotToPlace + 1;
|
|
||||||
listenersREF.lazySet(this, localListeners);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,31 +147,28 @@ class Subscription {
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
boolean unsubscribe(final Object listener) {
|
boolean unsubscribe(final Object listener) {
|
||||||
// single writer principle!
|
Entry entry = entries.get(listener);
|
||||||
|
if (entry == null || entry.getValue() == null) {
|
||||||
final Integer integer = listenerMap.remove(listener);
|
// fast exit
|
||||||
Object[] localListeners = listenersREF.get(this);
|
return false;
|
||||||
|
|
||||||
if (integer != null) {
|
|
||||||
final int index = integer;
|
|
||||||
firstFreeSpot = index;
|
|
||||||
localListeners[index] = null;
|
|
||||||
listenersREF.lazySet(this, localListeners);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = 0; i < localListeners.length; i++) {
|
// single writer principle!
|
||||||
if (localListeners[i] == listener) {
|
Entry head = headREF.get(this);
|
||||||
firstFreeSpot = i;
|
|
||||||
localListeners[i] = null;
|
|
||||||
listenersREF.lazySet(this, localListeners);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
firstFreeSpot = 0;
|
if (entry != head) {
|
||||||
return false;
|
entry.remove();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if it was second, now it's first
|
||||||
|
head = head.next();
|
||||||
|
//oldHead.clear(); // optimize for GC not possible because of potentially running iterators
|
||||||
|
}
|
||||||
|
|
||||||
|
this.entries.remove(listener);
|
||||||
|
headREF.lazySet(this, head);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -182,16 +176,7 @@ class Subscription {
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
int size() {
|
int size() {
|
||||||
// since this is ONLY used in unit tests, we count how many are non-null
|
return this.entries.size;
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
for (int i = 0; i < listeners.length; i++) {
|
|
||||||
if (listeners[i] != null) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
@ -200,62 +185,46 @@ class Subscription {
|
|||||||
final int handleIndex = this.handler.getMethodIndex();
|
final int handleIndex = this.handler.getMethodIndex();
|
||||||
final IHandlerInvocation invocation = this.invocation;
|
final IHandlerInvocation invocation = this.invocation;
|
||||||
|
|
||||||
|
Entry current = headREF.get(this);
|
||||||
Object listener;
|
Object listener;
|
||||||
final Object[] localListeners = listenersREF.get(this);
|
while (current != null) {
|
||||||
for (int i = 0; i < localListeners.length; i++) {
|
listener = current.getValue();
|
||||||
listener = localListeners[i];
|
current = current.next();
|
||||||
if (listener != null) {
|
|
||||||
invocation.invoke(listener, handler, handleIndex, message);
|
invocation.invoke(listener, handler, handleIndex, message);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
void publish(final Object message1, final Object message2) throws Throwable {
|
void publish(final Object message1, final Object message2) throws Throwable {
|
||||||
// final MethodAccess handler = this.handler.getHandler();
|
final MethodAccess handler = this.handler.getHandler();
|
||||||
// final int handleIndex = this.handler.getMethodIndex();
|
final int handleIndex = this.handler.getMethodIndex();
|
||||||
// final IHandlerInvocation invocation = this.invocation;
|
final IHandlerInvocation invocation = this.invocation;
|
||||||
//
|
|
||||||
// Iterator<Object> iterator;
|
Entry current = headREF.get(this);
|
||||||
// Object listener;
|
Object listener;
|
||||||
//
|
while (current != null) {
|
||||||
// for (iterator = this.listeners.iterator(); iterator.hasNext(); ) {
|
listener = current.getValue();
|
||||||
// listener = iterator.next();
|
current = current.next();
|
||||||
//
|
|
||||||
// invocation.invoke(listener, handler, handleIndex, message1, message2);
|
invocation.invoke(listener, handler, handleIndex, message1, message2);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
void publish(final Object message1, final Object message2, final Object message3) throws Throwable {
|
void publish(final Object message1, final Object message2, final Object message3) throws Throwable {
|
||||||
// final MethodAccess handler = this.handler.getHandler();
|
final MethodAccess handler = this.handler.getHandler();
|
||||||
// final int handleIndex = this.handler.getMethodIndex();
|
final int handleIndex = this.handler.getMethodIndex();
|
||||||
// final IHandlerInvocation invocation = this.invocation;
|
final IHandlerInvocation invocation = this.invocation;
|
||||||
//
|
|
||||||
// Iterator<Object> iterator;
|
|
||||||
// Object listener;
|
|
||||||
//
|
|
||||||
// for (iterator = this.listeners.iterator(); iterator.hasNext(); ) {
|
|
||||||
// listener = iterator.next();
|
|
||||||
//
|
|
||||||
// invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
Entry current = headREF.get(this);
|
||||||
void publish(final Object... messages) throws Throwable {
|
Object listener;
|
||||||
// final MethodAccess handler = this.handler.getHandler();
|
while (current != null) {
|
||||||
// final int handleIndex = this.handler.getMethodIndex();
|
listener = current.getValue();
|
||||||
// final IHandlerInvocation invocation = this.invocation;
|
current = current.next();
|
||||||
//
|
|
||||||
// Iterator<Object> iterator;
|
invocation.invoke(listener, handler, handleIndex, message1, message2, message3);
|
||||||
// Object listener;
|
}
|
||||||
//
|
|
||||||
// for (iterator = this.listeners.iterator(); iterator.hasNext(); ) {
|
|
||||||
// listener = iterator.next();
|
|
||||||
//
|
|
||||||
// invocation.invoke(listener, handler, handleIndex, messages);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,18 +16,16 @@
|
|||||||
package dorkbox.util.messagebus.subscription;
|
package dorkbox.util.messagebus.subscription;
|
||||||
|
|
||||||
import com.esotericsoftware.kryo.util.IdentityMap;
|
import com.esotericsoftware.kryo.util.IdentityMap;
|
||||||
import dorkbox.util.messagebus.common.HashMapTree;
|
import dorkbox.util.messagebus.common.ClassTree;
|
||||||
import dorkbox.util.messagebus.common.MessageHandler;
|
import dorkbox.util.messagebus.common.MessageHandler;
|
||||||
|
import dorkbox.util.messagebus.common.MultiClass;
|
||||||
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
||||||
import dorkbox.util.messagebus.utils.ClassUtils;
|
import dorkbox.util.messagebus.utils.ClassUtils;
|
||||||
import dorkbox.util.messagebus.utils.ReflectionUtils;
|
|
||||||
import dorkbox.util.messagebus.utils.SubscriptionUtils;
|
import dorkbox.util.messagebus.utils.SubscriptionUtils;
|
||||||
import dorkbox.util.messagebus.utils.VarArgUtils;
|
import dorkbox.util.messagebus.utils.VarArgUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
||||||
/**
|
/**
|
||||||
@ -42,6 +40,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
|
|||||||
* @author dorkbox, llc
|
* @author dorkbox, llc
|
||||||
* Date: 2/2/15
|
* Date: 2/2/15
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public final
|
public final
|
||||||
class SubscriptionManager {
|
class SubscriptionManager {
|
||||||
public static final float LOAD_FACTOR = 0.8F;
|
public static final float LOAD_FACTOR = 0.8F;
|
||||||
@ -65,6 +64,7 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
// all subscriptions of a message type.
|
// all subscriptions of a message type.
|
||||||
private volatile IdentityMap<Class<?>, Subscription[]> subsSingle;
|
private volatile IdentityMap<Class<?>, Subscription[]> subsSingle;
|
||||||
|
private volatile IdentityMap<MultiClass, Subscription[]> subsMulti;
|
||||||
|
|
||||||
// keeps track of all subscriptions of the super classes of a message type.
|
// keeps track of all subscriptions of the super classes of a message type.
|
||||||
private volatile IdentityMap<Class<?>, Subscription[]> subsSuperSingle;
|
private volatile IdentityMap<Class<?>, Subscription[]> subsSuperSingle;
|
||||||
@ -77,7 +77,7 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
// In order to force the "Single writer principle" on subscribe & unsubscribe, they are within WRITE LOCKS. They could be dispatched
|
// In order to force the "Single writer principle" on subscribe & unsubscribe, they are within WRITE LOCKS. They could be dispatched
|
||||||
// to another thread, however we do NOT want them asynchronous - as publish() should ALWAYS succeed if a correct subscribe() is
|
// to another thread, however we do NOT want them asynchronous - as publish() should ALWAYS succeed if a correct subscribe() is
|
||||||
// called before.
|
// called before. A WriteLock doesn't perform any better here than synchronized does.
|
||||||
private final Object singleWriterLock = new Object();
|
private final Object singleWriterLock = new Object();
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ class SubscriptionManager {
|
|||||||
private final SubscriptionUtils subUtils;
|
private final SubscriptionUtils subUtils;
|
||||||
private final VarArgUtils varArgUtils;
|
private final VarArgUtils varArgUtils;
|
||||||
|
|
||||||
private final HashMapTree<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageMulti;
|
private final ClassTree<Class<?>> classTree;
|
||||||
|
|
||||||
// shortcut publication if we know there is no possibility of varArg (ie: a method that has an array as arguments)
|
// shortcut publication if we know there is no possibility of varArg (ie: a method that has an array as arguments)
|
||||||
private final AtomicBoolean varArgPossibility = new AtomicBoolean(false);
|
private final AtomicBoolean varArgPossibility = new AtomicBoolean(false);
|
||||||
@ -102,6 +102,12 @@ class SubscriptionManager {
|
|||||||
IdentityMap.class,
|
IdentityMap.class,
|
||||||
"subsSingle");
|
"subsSingle");
|
||||||
|
|
||||||
|
private static final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsMultiREF =
|
||||||
|
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
||||||
|
IdentityMap.class,
|
||||||
|
"subsMulti");
|
||||||
|
|
||||||
|
|
||||||
private static final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsSuperSingleREF =
|
private static final AtomicReferenceFieldUpdater<SubscriptionManager, IdentityMap> subsSuperSingleREF =
|
||||||
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
AtomicReferenceFieldUpdater.newUpdater(SubscriptionManager.class,
|
||||||
IdentityMap.class,
|
IdentityMap.class,
|
||||||
@ -131,14 +137,16 @@ class SubscriptionManager {
|
|||||||
nonListeners = new IdentityMap<Class<?>, Boolean>(16, LOAD_FACTOR);
|
nonListeners = new IdentityMap<Class<?>, Boolean>(16, LOAD_FACTOR);
|
||||||
subsPerListener = new IdentityMap<>(32, LOAD_FACTOR);
|
subsPerListener = new IdentityMap<>(32, LOAD_FACTOR);
|
||||||
subsSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
subsSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
subsMulti = new IdentityMap<MultiClass, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
|
||||||
|
|
||||||
subsSuperSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
subsSuperSingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
subsVaritySingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
subsVaritySingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
subsSuperVaritySingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
subsSuperVaritySingle = new IdentityMap<Class<?>, Subscription[]>(32, LOAD_FACTOR);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.classTree = new ClassTree<Class<?>>();
|
||||||
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>();
|
|
||||||
|
|
||||||
this.subUtils = new SubscriptionUtils(classUtils, LOAD_FACTOR, numberOfThreads);
|
this.subUtils = new SubscriptionUtils(classUtils, LOAD_FACTOR, numberOfThreads);
|
||||||
|
|
||||||
@ -151,6 +159,22 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
public
|
public
|
||||||
void shutdown() {
|
void shutdown() {
|
||||||
|
|
||||||
|
// explicitly clear out the subscriptions
|
||||||
|
final IdentityMap.Entries<Class<?>, Subscription[]> entries = subsPerListener.entries();
|
||||||
|
for (IdentityMap.Entry<Class<?>, Subscription[]> entry : entries) {
|
||||||
|
final Subscription[] subscriptions = entry.value;
|
||||||
|
if (subscriptions != null) {
|
||||||
|
Subscription subscription;
|
||||||
|
|
||||||
|
for (int i = 0; i < subscriptions.length; i++) {
|
||||||
|
subscription = subscriptions[i];
|
||||||
|
subscription.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.nonListeners.clear();
|
this.nonListeners.clear();
|
||||||
|
|
||||||
this.subsPerListener.clear();
|
this.subsPerListener.clear();
|
||||||
@ -160,8 +184,7 @@ class SubscriptionManager {
|
|||||||
this.subsVaritySingle.clear();
|
this.subsVaritySingle.clear();
|
||||||
this.subsSuperVaritySingle.clear();
|
this.subsSuperVaritySingle.clear();
|
||||||
|
|
||||||
|
this.classTree.clear();
|
||||||
this.subscriptionsPerMessageMulti.clear();
|
|
||||||
|
|
||||||
this.classUtils.shutdown();
|
this.classUtils.shutdown();
|
||||||
}
|
}
|
||||||
@ -174,38 +197,6 @@ class SubscriptionManager {
|
|||||||
// section. Because of this, we can have unlimited reader threads all going at the same time, without contention (which is our
|
// section. Because of this, we can have unlimited reader threads all going at the same time, without contention (which is our
|
||||||
// use-case 99% of the time)
|
// use-case 99% of the time)
|
||||||
synchronized (singleWriterLock) {
|
synchronized (singleWriterLock) {
|
||||||
|
|
||||||
// when subscribing, this is a GREAT opportunity to figure out the classes/objects loaded -- their hierarchy, AND generate UUIDs
|
|
||||||
// for each CLASS that can be accessed. This then lets us lookup a UUID for each object that comes in -- if an ID is found (for
|
|
||||||
// any part of it's object hierarchy) -- it means that we have that listeners for that object. this is MUCH faster checking if
|
|
||||||
// we have subscriptions first (and failing).
|
|
||||||
//
|
|
||||||
// so during subscribe we can check "getUUID for all parameter.class accessed by this listener" -> then during publish "lookup
|
|
||||||
// UUID of incoming message.class" (+ it's super classes, if necessary) -> then check if UUID exists. If yes, then we know there
|
|
||||||
// are subs. if no - then it's a dead message.
|
|
||||||
//
|
|
||||||
// This lets us accomplish TWO things
|
|
||||||
// 1) be able quickly determine if there are dead messages
|
|
||||||
// 2) be able to create "multi-class" UUIDs, when two+ classes are represented (always) by the same UUID, by a clever mixing of
|
|
||||||
// the classes individual UUIDs.
|
|
||||||
//
|
|
||||||
// The generation of UUIDs happens ONLY during subscribe, and during publish they are looked up. This UUID can be a simple
|
|
||||||
// AtomicInteger that starts a MIN_VALUE and count's up.
|
|
||||||
|
|
||||||
|
|
||||||
// note: we can do PRE-STARTUP instrumentation (ie, BEFORE any classes are loaded by the classloader) and inject the UUID into
|
|
||||||
// every object (as a public static final field), then use reflection to look up this value. It would go something like this:
|
|
||||||
// 1) scan every class for annotations that match
|
|
||||||
// 2) for each method that has our annotation -- get the list of classes + hierarchy that are the parameters for the method
|
|
||||||
// 3) inject the UUID field into each class object that #2 returns, only if it doesn't already exist. use invalid field names
|
|
||||||
// (ie: start with numbers or ? or ^ or something
|
|
||||||
//
|
|
||||||
// then during SUB/UNSUB/PUB, we use this UUID for everything (and we can have multi-UUID lookups for the 'multi-arg' thing).
|
|
||||||
// If there is no UUID, then we just abort the SUB/UNSUB or send a deadmessage
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final IdentityMap<Class<?>, Boolean> nonListeners = this.nonListeners;
|
final IdentityMap<Class<?>, Boolean> nonListeners = this.nonListeners;
|
||||||
if (nonListeners.containsKey(listenerClass)) {
|
if (nonListeners.containsKey(listenerClass)) {
|
||||||
// early reject of known classes that do not define message handlers
|
// early reject of known classes that do not define message handlers
|
||||||
@ -231,7 +222,9 @@ class SubscriptionManager {
|
|||||||
subscriptions = new Subscription[handlersSize];
|
subscriptions = new Subscription[handlersSize];
|
||||||
|
|
||||||
// access a snapshot of the subscriptions (single-writer-principle)
|
// access a snapshot of the subscriptions (single-writer-principle)
|
||||||
final IdentityMap<Class<?>, Subscription[]> localSubs = subsSingleREF.get(this);
|
final IdentityMap<Class<?>, Subscription[]> singleSubs = subsSingleREF.get(this);
|
||||||
|
// final IdentityMap<MultiClass, Subscription[]> multiSubs = subsMultiREF.get(this);
|
||||||
|
|
||||||
// final IdentityMap<Class<?>, Subscription[]> localSuperSubs = subsSuperSingleREF.get(this);
|
// final IdentityMap<Class<?>, Subscription[]> localSuperSubs = subsSuperSingleREF.get(this);
|
||||||
// final IdentityMap<Class<?>, Subscription[]> localVaritySubs = subsVaritySingleREF.get(this);
|
// final IdentityMap<Class<?>, Subscription[]> localVaritySubs = subsVaritySingleREF.get(this);
|
||||||
// final IdentityMap<Class<?>, Subscription[]> localSuperVaritySubs = subsSuperVaritySingleREF.get(this);
|
// final IdentityMap<Class<?>, Subscription[]> localSuperVaritySubs = subsSuperVaritySingleREF.get(this);
|
||||||
@ -242,24 +235,73 @@ class SubscriptionManager {
|
|||||||
Class<?>[] messageHandlerTypes;
|
Class<?>[] messageHandlerTypes;
|
||||||
Class<?> handlerType;
|
Class<?> handlerType;
|
||||||
|
|
||||||
|
|
||||||
|
// Prepare all of the subscriptions
|
||||||
for (int i = 0; i < handlersSize; i++) {
|
for (int i = 0; i < handlersSize; i++) {
|
||||||
// THE HANDLER IS THE SAME FOR ALL SUBSCRIPTIONS OF THE SAME TYPE!
|
// THE HANDLER IS THE SAME FOR ALL SUBSCRIPTIONS OF THE SAME TYPE!
|
||||||
messageHandler = messageHandlers[i];
|
messageHandler = messageHandlers[i];
|
||||||
|
|
||||||
// is this handler able to accept var args?
|
// is this handler able to accept var args?
|
||||||
if (messageHandler.getVarArgClass() != null) {
|
// if (messageHandler.getVarArgClass() != null) {
|
||||||
varArgPossibility.lazySet(true);
|
// varArgPossibility.lazySet(true);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// now create a list of subscriptions for this specific handlerType (but don't add anything yet).
|
// now create a list of subscriptions for this specific handlerType (but don't add anything yet).
|
||||||
// we only store things based on the FIRST type (for lookup) then parse the rest of the types during publication
|
// we only store things based on the FIRST type (for lookup) then parse the rest of the types during publication
|
||||||
messageHandlerTypes = messageHandler.getHandledMessages();
|
messageHandlerTypes = messageHandler.getHandledMessages();
|
||||||
handlerType = messageHandlerTypes[0];
|
// final int handlerSize = messageHandlerTypes.length;
|
||||||
|
// switch (handlerSize) {
|
||||||
|
// case 0: {
|
||||||
|
// // if a publisher publishes VOID, it calls a method with 0 parameters (that's been subscribed)
|
||||||
|
// // This is the SAME THING as having Void as a parameter!!
|
||||||
|
// handlerType = Void.class;
|
||||||
|
//
|
||||||
|
// if (!singleSubs.containsKey(handlerType)) {
|
||||||
|
// // this is copied to a larger array if necessary, but needs to be SOMETHING before subsPerListener is added
|
||||||
|
// singleSubs.put(handlerType, SUBSCRIPTIONS);
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case 1: {
|
||||||
|
handlerType = messageHandlerTypes[0];
|
||||||
|
|
||||||
if (!localSubs.containsKey(handlerType)) {
|
if (!singleSubs.containsKey(handlerType)) {
|
||||||
// this is copied to a larger array if necessary, but needs to be SOMETHING before subsPerListener is added
|
// this is copied to a larger array if necessary, but needs to be SOMETHING before subsPerListener is added
|
||||||
localSubs.put(handlerType, SUBSCRIPTIONS);
|
singleSubs.put(handlerType, SUBSCRIPTIONS);
|
||||||
}
|
}
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case 2: {
|
||||||
|
// final MultiClass multiClass = classTree.get(messageHandlerTypes[0],
|
||||||
|
// messageHandlerTypes[1]);
|
||||||
|
//
|
||||||
|
// if (!multiSubs.containsKey(multiClass)) {
|
||||||
|
// // this is copied to a larger array if necessary, but needs to be SOMETHING before subsPerListener is added
|
||||||
|
// multiSubs.put(multiClass, SUBSCRIPTIONS);
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// case 3: {
|
||||||
|
// final MultiClass multiClass = classTree.get(messageHandlerTypes[0],
|
||||||
|
// messageHandlerTypes[1],
|
||||||
|
// messageHandlerTypes[2]);
|
||||||
|
//
|
||||||
|
// if (!multiSubs.containsKey(multiClass)) {
|
||||||
|
// // this is copied to a larger array if necessary, but needs to be SOMETHING before subsPerListener is added
|
||||||
|
// multiSubs.put(multiClass, SUBSCRIPTIONS);
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// default: {
|
||||||
|
// final MultiClass multiClass = classTree.get(messageHandlerTypes);
|
||||||
|
//
|
||||||
|
// if (!multiSubs.containsKey(multiClass)) {
|
||||||
|
// // this is copied to a larger array if necessary, but needs to be SOMETHING before subsPerListener is added
|
||||||
|
// multiSubs.put(multiClass, SUBSCRIPTIONS);
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// create the subscription. This can be thrown away if the subscription succeeds in another thread
|
// create the subscription. This can be thrown away if the subscription succeeds in another thread
|
||||||
subscription = new Subscription(listenerClass, messageHandler);
|
subscription = new Subscription(listenerClass, messageHandler);
|
||||||
@ -272,7 +314,7 @@ class SubscriptionManager {
|
|||||||
subsPerListener.put(listenerClass, subscriptions);
|
subsPerListener.put(listenerClass, subscriptions);
|
||||||
|
|
||||||
|
|
||||||
// we can now safely add for publication AND subscribe since the data structures are consistent
|
// add for publication AND subscribe since the data structures are consistent
|
||||||
for (int i = 0; i < handlersSize; i++) {
|
for (int i = 0; i < handlersSize; i++) {
|
||||||
subscription = subscriptions[i];
|
subscription = subscriptions[i];
|
||||||
subscription.subscribe(listener); // register this callback listener to this subscription
|
subscription.subscribe(listener); // register this callback listener to this subscription
|
||||||
@ -282,28 +324,100 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
// register for publication
|
// register for publication
|
||||||
messageHandlerTypes = messageHandler.getHandledMessages();
|
messageHandlerTypes = messageHandler.getHandledMessages();
|
||||||
handlerType = messageHandlerTypes[0];
|
// final int handlerSize = messageHandlerTypes.length;
|
||||||
|
//
|
||||||
|
// switch (handlerSize) {
|
||||||
|
// case 0: {
|
||||||
|
// handlerType = Void.class;
|
||||||
|
//
|
||||||
|
// // makes this subscription visible for publication
|
||||||
|
// final Subscription[] currentSubs = singleSubs.get(handlerType);
|
||||||
|
// final int currentLength = currentSubs.length;
|
||||||
|
//
|
||||||
|
// // add the new subscription to the array
|
||||||
|
// final Subscription[] newSubs = Arrays.copyOf(currentSubs, currentLength + 1, Subscription[].class);
|
||||||
|
// newSubs[currentLength] = subscription;
|
||||||
|
// singleSubs.put(handlerType, newSubs);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// case 1: {
|
||||||
|
handlerType = messageHandlerTypes[0];
|
||||||
|
|
||||||
|
// makes this subscription visible for publication
|
||||||
|
final Subscription[] currentSubs = singleSubs.get(handlerType);
|
||||||
|
final int currentLength = currentSubs.length;
|
||||||
|
|
||||||
|
// add the new subscription to the array
|
||||||
|
final Subscription[] newSubs = Arrays.copyOf(currentSubs, currentLength + 1, Subscription[].class);
|
||||||
|
newSubs[currentLength] = subscription;
|
||||||
|
singleSubs.put(handlerType, newSubs);
|
||||||
|
|
||||||
|
|
||||||
// makes this subscription visible for publication
|
// update the varity/super types
|
||||||
final Subscription[] currentSubs = localSubs.get(handlerType);
|
// registerExtraSubs(handlerType, singleSubs, localSuperSubs, localVaritySubs);
|
||||||
final int currentLength = currentSubs.length;
|
|
||||||
|
|
||||||
// add the new subscription to the array
|
// break;
|
||||||
final Subscription[] newSubs = Arrays.copyOf(currentSubs, currentLength + 1, Subscription[].class);
|
// }
|
||||||
newSubs[currentLength] = subscription;
|
//
|
||||||
localSubs.put(handlerType, newSubs);
|
// case 2: {
|
||||||
|
// final MultiClass multiClass = classTree.get(messageHandlerTypes[0],
|
||||||
// update the varity/super types
|
// messageHandlerTypes[1]);
|
||||||
// registerExtraSubs(handlerType, localSubs, localSuperSubs, localVaritySubs);
|
// // makes this subscription visible for publication
|
||||||
|
// final Subscription[] currentSubs = multiSubs.get(multiClass);
|
||||||
|
// final int currentLength = currentSubs.length;
|
||||||
|
//
|
||||||
|
// // add the new subscription to the array
|
||||||
|
// final Subscription[] newSubs = Arrays.copyOf(currentSubs, currentLength + 1, Subscription[].class);
|
||||||
|
// newSubs[currentLength] = subscription;
|
||||||
|
// multiSubs.put(multiClass, newSubs);
|
||||||
|
//
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// case 3: {
|
||||||
|
// final MultiClass multiClass = classTree.get(messageHandlerTypes[0],
|
||||||
|
// messageHandlerTypes[1],
|
||||||
|
// messageHandlerTypes[2]);
|
||||||
|
// // makes this subscription visible for publication
|
||||||
|
// final Subscription[] currentSubs = multiSubs.get(multiClass);
|
||||||
|
// final int currentLength = currentSubs.length;
|
||||||
|
//
|
||||||
|
// // add the new subscription to the array
|
||||||
|
// final Subscription[] newSubs = Arrays.copyOf(currentSubs, currentLength + 1, Subscription[].class);
|
||||||
|
// newSubs[currentLength] = subscription;
|
||||||
|
// multiSubs.put(multiClass, newSubs);
|
||||||
|
//
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// default: {
|
||||||
|
// final MultiClass multiClass = classTree.get(messageHandlerTypes);
|
||||||
|
// // makes this subscription visible for publication
|
||||||
|
// final Subscription[] currentSubs = multiSubs.get(multiClass);
|
||||||
|
// final int currentLength = currentSubs.length;
|
||||||
|
//
|
||||||
|
// // add the new subscription to the array
|
||||||
|
// final Subscription[] newSubs = Arrays.copyOf(currentSubs, currentLength + 1, Subscription[].class);
|
||||||
|
// newSubs[currentLength] = subscription;
|
||||||
|
// multiSubs.put(multiClass, newSubs);
|
||||||
|
//
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// save this snapshot back to the original (single writer principle)
|
// save this snapshot back to the original (single writer principle)
|
||||||
subsSingleREF.lazySet(this, localSubs);
|
subsSingleREF.lazySet(this, singleSubs);
|
||||||
|
// subsMultiREF.lazySet(this, multiSubs);
|
||||||
// subsSuperSingleREF.lazySet(this, localSuperSubs);
|
// subsSuperSingleREF.lazySet(this, localSuperSubs);
|
||||||
// subsVaritySingleREF.lazySet(this, localVaritySubs);
|
// subsVaritySingleREF.lazySet(this, localVaritySubs);
|
||||||
// subsSuperVaritySingleREF.lazySet(this, localSuperVaritySubs);
|
// subsSuperVaritySingleREF.lazySet(this, localSuperVaritySubs);
|
||||||
|
|
||||||
|
|
||||||
|
// only dump the super subscritions if it is a COMPLETELY NEW subscription. If it's not new, then the heirarchy isn't
|
||||||
|
// changing for super subscriptions
|
||||||
|
subsSuperSingleREF.lazySet(this, new IdentityMap(32));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// subscriptions already exist and must only be updated
|
// subscriptions already exist and must only be updated
|
||||||
@ -425,85 +539,44 @@ class SubscriptionManager {
|
|||||||
return varArgUtils;
|
return varArgUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
// inside a write lock
|
|
||||||
// add this subscription to each of the handled types
|
|
||||||
// to activate this sub for publication
|
|
||||||
private
|
|
||||||
void registerMulti(final Subscription subscription, final Class<?> listenerClass,
|
|
||||||
final Map<Class<?>, List<Subscription>> subsPerMessageSingle,
|
|
||||||
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti, final AtomicBoolean varArgPossibility) {
|
|
||||||
|
|
||||||
final MessageHandler handler = subscription.getHandler();
|
|
||||||
final Class<?>[] messageHandlerTypes = handler.getHandledMessages();
|
|
||||||
final int size = messageHandlerTypes.length;
|
|
||||||
|
|
||||||
final Class<?> type0 = messageHandlerTypes[0];
|
|
||||||
|
|
||||||
switch (size) {
|
|
||||||
case 0: {
|
|
||||||
// TODO: maybe this SHOULD be permitted? so if a publisher publishes VOID, it call's a method?
|
|
||||||
errorHandler.handleError("Error while trying to subscribe class with zero arguments", listenerClass);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
// // using ThreadLocal cache's is SIGNIFICANTLY faster for subscribing to new types
|
|
||||||
// final List<Subscription> cachedSubs = listCache.get();
|
|
||||||
// List<Subscription> subs = subsPerMessageSingle.putIfAbsent(type0, cachedSubs);
|
|
||||||
// if (subs == null) {
|
|
||||||
// listCache.set(new CopyOnWriteArrayList<Subscription>());
|
|
||||||
//// listCache.set(new ArrayList<Subscription>(8));
|
|
||||||
// subs = cachedSubs;
|
|
||||||
//
|
|
||||||
// // is this handler able to accept var args?
|
|
||||||
// if (handler.getVarArgClass() != null) {
|
|
||||||
// varArgPossibility.lazySet(true);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// subs.add(subscription);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
ArrayList<Subscription> subs = subsPerMessageMulti.get(type0, messageHandlerTypes[1]);
|
|
||||||
if (subs == null) {
|
|
||||||
subs = new ArrayList<Subscription>();
|
|
||||||
|
|
||||||
subsPerMessageMulti.put(subs, type0, messageHandlerTypes[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
subs.add(subscription);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 3: {
|
|
||||||
ArrayList<Subscription> subs = subsPerMessageMulti.get(type0, messageHandlerTypes[1], messageHandlerTypes[2]);
|
|
||||||
if (subs == null) {
|
|
||||||
subs = new ArrayList<Subscription>();
|
|
||||||
|
|
||||||
subsPerMessageMulti.put(subs, type0, messageHandlerTypes[1], messageHandlerTypes[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
subs.add(subscription);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
ArrayList<Subscription> subs = subsPerMessageMulti.get(messageHandlerTypes);
|
|
||||||
if (subs == null) {
|
|
||||||
subs = new ArrayList<Subscription>();
|
|
||||||
|
|
||||||
subsPerMessageMulti.put(subs, messageHandlerTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
subs.add(subscription);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
// can return null
|
||||||
public
|
public
|
||||||
Subscription[] getSubs(final Class<?> messageClass) {
|
Subscription[] getSubs(final Class<?> messageClass) {
|
||||||
return (Subscription[]) subsSingleREF.get(this).get(messageClass);
|
return (Subscription[]) subsSingleREF.get(this).get(messageClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2) {
|
||||||
|
// never returns null
|
||||||
|
final MultiClass multiClass = classTree.get(messageClass1,
|
||||||
|
messageClass2);
|
||||||
|
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
|
// never returns null
|
||||||
|
final MultiClass multiClass = classTree.get(messageClass1,
|
||||||
|
messageClass2,
|
||||||
|
messageClass3);
|
||||||
|
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getSubs(final Class<?>[] messageClasses) {
|
||||||
|
// never returns null
|
||||||
|
final MultiClass multiClass = classTree.get(messageClasses);
|
||||||
|
|
||||||
|
return (Subscription[]) subsMultiREF.get(this).get(multiClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// can return null
|
// can return null
|
||||||
public
|
public
|
||||||
Subscription[] getSuperSubs(final Class<?> messageClass) {
|
Subscription[] getSuperSubs(final Class<?> messageClass) {
|
||||||
@ -514,8 +587,8 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
final IdentityMap<Class<?>, Subscription[]> localSubs = subsSingleREF.get(this);
|
final IdentityMap<Class<?>, Subscription[]> localSubs = subsSingleREF.get(this);
|
||||||
|
|
||||||
Subscription sub;
|
|
||||||
Class<?> superClass;
|
Class<?> superClass;
|
||||||
|
Subscription sub;
|
||||||
Subscription[] superSubs;
|
Subscription[] superSubs;
|
||||||
boolean hasSubs = false;
|
boolean hasSubs = false;
|
||||||
|
|
||||||
@ -543,184 +616,144 @@ class SubscriptionManager {
|
|||||||
return subsAsList.toArray(new Subscription[0]);
|
return subsAsList.toArray(new Subscription[0]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// TODO: shortcut out if there are no handlers that accept subtypes
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// IT IS CRITICAL TO REMEMBER: The subscriptions that are remembered here DO NOT CHANGE (only the listeners inside then change)
|
// IT IS CRITICAL TO REMEMBER: The subscriptions that are remembered here DO NOT CHANGE (only the listeners inside them change). if we
|
||||||
|
// subscribe a super/child class -- THEN these subscriptions change!
|
||||||
|
|
||||||
// return (Subscription[]) subsSuperSingleREF.get(this).get(messageClass);
|
// return (Subscription[]) subsSuperSingleREF.get(this).get(messageClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
ArrayList<Subscription> getExactAsArray(final Class<?> messageClass1, final Class<?> messageClass2) {
|
|
||||||
return subscriptionsPerMessageMulti.get(messageClass1, messageClass2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
ArrayList<Subscription> getExactAsArray(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
|
||||||
return subscriptionsPerMessageMulti.get(messageClass1, messageClass2, messageClass3);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// can return null
|
// can return null
|
||||||
public
|
public
|
||||||
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2) {
|
Subscription[] getSuperSubs(final Class<?> messageClass1, final Class<?> messageClass2) {
|
||||||
final ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2);
|
// save the subscriptions
|
||||||
|
final Class<?>[] superClasses1 = this.classUtils.getSuperClasses(messageClass1); // never returns null, cached response
|
||||||
|
final Class<?>[] superClasses2 = this.classUtils.getSuperClasses(messageClass2); // never returns null, cached response
|
||||||
|
|
||||||
if (collection != null) {
|
final IdentityMap<MultiClass, Subscription[]> localSubs = subsMultiREF.get(this);
|
||||||
return collection.toArray(new Subscription[0]);
|
|
||||||
|
Class<?> superClass1;
|
||||||
|
Class<?> superClass2;
|
||||||
|
Subscription sub;
|
||||||
|
Subscription[] superSubs;
|
||||||
|
boolean hasSubs = false;
|
||||||
|
|
||||||
|
|
||||||
|
final int length1 = superClasses1.length;
|
||||||
|
final int length2 = superClasses2.length;
|
||||||
|
|
||||||
|
ArrayList<Subscription> subsAsList = new ArrayList<Subscription>(length1 + length2);
|
||||||
|
|
||||||
|
for (int i = 0; i < length1; i++) {
|
||||||
|
superClass1 = superClasses1[i];
|
||||||
|
|
||||||
|
// only go over subtypes
|
||||||
|
if (superClass1 == messageClass1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < length2; j++) {
|
||||||
|
superClass2 = superClasses2[j];
|
||||||
|
|
||||||
|
// only go over subtypes
|
||||||
|
if (superClass2 == messageClass2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// never returns null
|
||||||
|
final MultiClass multiClass = classTree.get(superClass1,
|
||||||
|
superClass2);
|
||||||
|
superSubs = localSubs.get(multiClass);
|
||||||
|
if (superSubs != null) {
|
||||||
|
for (int k = 0; k < superSubs.length; k++) {
|
||||||
|
sub = superSubs[k];
|
||||||
|
|
||||||
|
if (sub.getHandler().acceptsSubtypes()) {
|
||||||
|
subsAsList.add(sub);
|
||||||
|
hasSubs = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
// subsAsList now contains ALL of the super-class subscriptions.
|
||||||
}
|
if (hasSubs) {
|
||||||
|
return subsAsList.toArray(new Subscription[0]);
|
||||||
// can return null
|
}
|
||||||
public
|
else {
|
||||||
Subscription[] getSubs(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
// TODO: shortcut out if there are no handlers that accept subtypes
|
||||||
|
return null;
|
||||||
final ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3);
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
return collection.toArray(new Subscription[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
|
||||||
public
|
|
||||||
Subscription[] getExactAndSuper_MUST_HAVE_SEPERATE_CALLS(final Class<?> messageClass) {
|
|
||||||
Subscription[] collection = getSubs(messageClass); // can return null
|
|
||||||
|
|
||||||
// now superClasses
|
|
||||||
Class[] types = ReflectionUtils.getSuperTypes(messageClass);
|
|
||||||
|
|
||||||
// NOTE: have to recalculate SUPER classes for the specified class, because WE DO NOT know this class during subscription time!
|
|
||||||
|
|
||||||
|
|
||||||
// whenever our subscriptions change, this map is cleared.
|
|
||||||
// final Map<Class<?>, ArrayList<Subscription>> local = this.superClassSubscriptions;
|
|
||||||
|
|
||||||
// ArrayList<Subscription> subs = local.get(clazz);
|
|
||||||
//
|
|
||||||
// if (subs == null) {
|
|
||||||
// // types was not empty, so collect subscriptions for each type and collate them
|
|
||||||
//
|
|
||||||
// // save the subscriptions
|
|
||||||
// final Class<?>[] superClasses = this.superClass.getSuperClasses(clazz); // never returns null, cached response
|
|
||||||
//
|
|
||||||
// Class<?> superClass;
|
|
||||||
// ArrayList<Subscription> superSubs;
|
|
||||||
// Subscription sub;
|
|
||||||
//
|
|
||||||
// final int length = superClasses.length;
|
|
||||||
// int superSubLength;
|
|
||||||
// subs = new ArrayList<Subscription>(length);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < length; i++) {
|
|
||||||
// superClass = superClasses[i];
|
|
||||||
// superSubs = subscriber.getExactAsArray(superClass);
|
|
||||||
//
|
|
||||||
// if (superSubs != null) {
|
|
||||||
// superSubLength = superSubs.size();
|
|
||||||
// for (int j = 0; j < superSubLength; j++) {
|
|
||||||
// sub = superSubs.get(j);
|
|
||||||
//
|
|
||||||
// if (sub.getHandler().acceptsSubtypes()) {
|
|
||||||
// subs.add(sub);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// subs.trimToSize();
|
|
||||||
// local.put(clazz, subs);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return subs;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// final Subscription[] superSubscriptions = getSuperSubs(messageClass); // can return null
|
|
||||||
//
|
|
||||||
// if (collection != null) {
|
|
||||||
// if (superSubscriptions != null) {
|
|
||||||
// // but both into a single array
|
|
||||||
// final int length = collection.length;
|
|
||||||
// final int lengthSuper = superSubscriptions.length;
|
|
||||||
//
|
|
||||||
// final Subscription[] newSubs = new Subscription[length + lengthSuper];
|
|
||||||
// System.arraycopy(collection, 0, newSubs, 0, length);
|
|
||||||
// System.arraycopy(superSubscriptions, 0, newSubs, length, lengthSuper);
|
|
||||||
//
|
|
||||||
// return newSubs;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return collection;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return superSubscriptions;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
// can return null
|
||||||
public
|
public
|
||||||
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2) {
|
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2) {
|
||||||
ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2); // can return null
|
// ArrayList<Subscription> collection = getSubs(messageClass1, messageClass2); // can return null
|
||||||
|
//
|
||||||
// now publish superClasses
|
// // now publish superClasses
|
||||||
final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2,
|
// final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2,
|
||||||
this); // NOT return null
|
// this); // NOT return null
|
||||||
|
//
|
||||||
if (collection != null) {
|
// if (collection != null) {
|
||||||
collection = new ArrayList<Subscription>(collection);
|
// collection = new ArrayList<Subscription>(collection);
|
||||||
|
//
|
||||||
if (!superSubs.isEmpty()) {
|
// if (!superSubs.isEmpty()) {
|
||||||
collection.addAll(superSubs);
|
// collection.addAll(superSubs);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else if (!superSubs.isEmpty()) {
|
// else if (!superSubs.isEmpty()) {
|
||||||
collection = superSubs;
|
// collection = superSubs;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (collection != null) {
|
// if (collection != null) {
|
||||||
return collection.toArray(new Subscription[0]);
|
// return collection.toArray(new Subscription[0]);
|
||||||
}
|
// }
|
||||||
else {
|
// else {
|
||||||
return null;
|
return null;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// can return null
|
// can return null
|
||||||
public
|
public
|
||||||
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
|
//
|
||||||
ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3); // can return null
|
// ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3); // can return null
|
||||||
|
//
|
||||||
// now publish superClasses
|
// // now publish superClasses
|
||||||
final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2, messageClass3,
|
// final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2, messageClass3,
|
||||||
this); // NOT return null
|
// this); // NOT return null
|
||||||
|
//
|
||||||
if (collection != null) {
|
// if (collection != null) {
|
||||||
collection = new ArrayList<Subscription>(collection);
|
// collection = new ArrayList<Subscription>(collection);
|
||||||
|
//
|
||||||
if (!superSubs.isEmpty()) {
|
// if (!superSubs.isEmpty()) {
|
||||||
collection.addAll(superSubs);
|
// collection.addAll(superSubs);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else if (!superSubs.isEmpty()) {
|
// else if (!superSubs.isEmpty()) {
|
||||||
collection = superSubs;
|
// collection = superSubs;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (collection != null) {
|
// if (collection != null) {
|
||||||
return collection.toArray(new Subscription[0]);
|
// return collection.toArray(new Subscription[0]);
|
||||||
}
|
// }
|
||||||
else {
|
// else {
|
||||||
return null;
|
return null;
|
||||||
}
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public
|
||||||
|
Subscription[] getSuperSubs(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user