Fixed issues with varity argumuent subscriptions, cleaned up model, removed "FirstArg" publisher (didn't make any sense. Publication should always use all arguments)
This commit is contained in:
parent
be960b6913
commit
0ed9b1b243
@ -112,23 +112,11 @@ public interface IMessageBus extends PubSubSupport {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Will publish to listeners with this exact message signature, as well as listeners that match the super class types signatures.
|
* Will publish to listeners with this exact message signature, as well as listeners that match the super class types signatures.
|
||||||
* and to listeners that have matching varity arguments. (ie: a listener that matches Obeject[], will accept messages of type Object)
|
* and to listeners that have matching varity arguments. (ie: a listener that matches Object[], will accept messages of type Object)
|
||||||
*/
|
*/
|
||||||
ExactWithSuperTypesAndVarArgs,
|
ExactWithSuperTypesAndVarity,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SubscribeMode {
|
|
||||||
/**
|
|
||||||
* Will subscribe and publish using all provided parameters in the method signature (for subscribe), and arguments (for publish)
|
|
||||||
*/
|
|
||||||
MultiArg,
|
|
||||||
/**
|
|
||||||
* Will subscribe and publish using only the FIRST provided parameter in the method signature (for subscribe), and arguments (for publish)
|
|
||||||
*/
|
|
||||||
FirstArg,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether any asynchronous message publications are pending to be processed
|
* Check whether any asynchronous message publications are pending to be processed
|
||||||
*
|
*
|
||||||
|
@ -30,13 +30,9 @@ import dorkbox.util.messagebus.error.DefaultErrorHandler;
|
|||||||
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
||||||
import dorkbox.util.messagebus.error.PublicationError;
|
import dorkbox.util.messagebus.error.PublicationError;
|
||||||
import dorkbox.util.messagebus.publication.Publisher;
|
import dorkbox.util.messagebus.publication.Publisher;
|
||||||
import dorkbox.util.messagebus.publication.PublisherAll_MultiArg;
|
import dorkbox.util.messagebus.publication.PublisherExact;
|
||||||
import dorkbox.util.messagebus.publication.PublisherExactWithSuperTypes_FirstArg;
|
import dorkbox.util.messagebus.publication.PublisherExactWithSuperTypes;
|
||||||
import dorkbox.util.messagebus.publication.PublisherExactWithSuperTypes_MultiArg;
|
import dorkbox.util.messagebus.publication.PublisherExactWithSuperTypesAndVarity;
|
||||||
import dorkbox.util.messagebus.publication.PublisherExact_FirstArg;
|
|
||||||
import dorkbox.util.messagebus.publication.PublisherExact_MultiArg;
|
|
||||||
import dorkbox.util.messagebus.subscription.FirstArgSubscriber;
|
|
||||||
import dorkbox.util.messagebus.subscription.MultiArgSubscriber;
|
|
||||||
import dorkbox.util.messagebus.subscription.Subscriber;
|
import dorkbox.util.messagebus.subscription.Subscriber;
|
||||||
import dorkbox.util.messagebus.subscription.SubscriptionManager;
|
import dorkbox.util.messagebus.subscription.SubscriptionManager;
|
||||||
import dorkbox.util.messagebus.utils.ClassUtils;
|
import dorkbox.util.messagebus.utils.ClassUtils;
|
||||||
@ -62,7 +58,7 @@ class MessageBus implements IMessageBus {
|
|||||||
private final ClassUtils classUtils;
|
private final ClassUtils classUtils;
|
||||||
private final SubscriptionManager subscriptionManager;
|
private final SubscriptionManager subscriptionManager;
|
||||||
|
|
||||||
private final Publisher subscriptionPublisher;
|
private final Publisher publisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the consumers during shutdown, that it's on purpose.
|
* Notifies the consumers during shutdown, that it's on purpose.
|
||||||
@ -74,30 +70,40 @@ class MessageBus implements IMessageBus {
|
|||||||
private Sequence workSequence;
|
private Sequence workSequence;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* By default, will permit subTypes and VarArg matching, and will use half of CPUs available for dispatching async messages
|
* By default, will permit subTypes and Varity Argument matching, and will use half of CPUs available for dispatching async messages
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
MessageBus() {
|
MessageBus() {
|
||||||
this(Runtime.getRuntime().availableProcessors());
|
this(Runtime.getRuntime().availableProcessors()/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param numberOfThreads how many threads to have for dispatching async messages
|
* By default, will permit subTypes and Varity Argument matching
|
||||||
|
*
|
||||||
|
* @param numberOfThreads how many threads to use for dispatching async messages
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
MessageBus(int numberOfThreads) {
|
MessageBus(int numberOfThreads) {
|
||||||
this(PublishMode.ExactWithSuperTypes, SubscribeMode.FirstArg, numberOfThreads);
|
this(PublishMode.ExactWithSuperTypesAndVarity, numberOfThreads);
|
||||||
// this(PublishMode.ExactWithSuperTypes, SubscribeMode.MultiArg, numberOfThreads);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* By default, will use half of CPUs available for dispatching async messages
|
||||||
|
*
|
||||||
* @param publishMode Specifies which publishMode to operate the publication of messages.
|
* @param publishMode Specifies which publishMode to operate the publication of messages.
|
||||||
* @param numberOfThreads how many threads to have for dispatching async messages
|
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
MessageBus(final PublishMode publishMode, final SubscribeMode subscribeMode, int numberOfThreads) {
|
MessageBus(final PublishMode publishMode) {
|
||||||
|
this(publishMode, Runtime.getRuntime().availableProcessors()/2);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param publishMode Specifies which publishMode to operate the publication of messages.
|
||||||
|
* @param numberOfThreads how many threads to use for dispatching async messages
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
MessageBus(final PublishMode publishMode, int numberOfThreads) {
|
||||||
// round to the nearest power of 2
|
// round to the nearest power of 2
|
||||||
numberOfThreads = 1 << (32 - Integer.numberOfLeadingZeros(getMinNumberOfThreads(numberOfThreads) - 1));
|
numberOfThreads = 1 << (32 - Integer.numberOfLeadingZeros(getMinNumberOfThreads(numberOfThreads)));
|
||||||
|
|
||||||
this.errorHandler = new DefaultErrorHandler();
|
this.errorHandler = new DefaultErrorHandler();
|
||||||
// this.dispatchQueue = new ArrayBlockingQueue<Object>(6);
|
// this.dispatchQueue = new ArrayBlockingQueue<Object>(6);
|
||||||
@ -106,43 +112,25 @@ class MessageBus implements IMessageBus {
|
|||||||
|
|
||||||
final StampedLock lock = new StampedLock();
|
final StampedLock lock = new StampedLock();
|
||||||
|
|
||||||
boolean isMultiArg = subscribeMode == SubscribeMode.MultiArg;
|
|
||||||
|
|
||||||
final Subscriber subscriber;
|
final Subscriber subscriber;
|
||||||
if (isMultiArg) {
|
/**
|
||||||
subscriber = new MultiArgSubscriber(errorHandler, classUtils);
|
* Will subscribe and publish using all provided parameters in the method signature (for subscribe), and arguments (for publish)
|
||||||
}
|
*/
|
||||||
else {
|
subscriber = new Subscriber(errorHandler, classUtils);
|
||||||
subscriber = new FirstArgSubscriber(errorHandler, classUtils);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (publishMode) {
|
switch (publishMode) {
|
||||||
case Exact:
|
case Exact:
|
||||||
if (isMultiArg) {
|
publisher = new PublisherExact(errorHandler, subscriber, lock);
|
||||||
subscriptionPublisher = new PublisherExact_MultiArg(errorHandler, subscriber, lock);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
subscriptionPublisher = new PublisherExact_FirstArg(errorHandler, subscriber, lock);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExactWithSuperTypes:
|
case ExactWithSuperTypes:
|
||||||
if (isMultiArg) {
|
publisher = new PublisherExactWithSuperTypes(errorHandler, subscriber, lock);
|
||||||
subscriptionPublisher = new PublisherExactWithSuperTypes_MultiArg(errorHandler, subscriber, lock);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
subscriptionPublisher = new PublisherExactWithSuperTypes_FirstArg(errorHandler, subscriber, lock);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExactWithSuperTypesAndVarArgs:
|
case ExactWithSuperTypesAndVarity:
|
||||||
default:
|
default:
|
||||||
if (isMultiArg) {
|
publisher = new PublisherExactWithSuperTypesAndVarity(errorHandler, subscriber, lock);
|
||||||
subscriptionPublisher = new PublisherAll_MultiArg(errorHandler, subscriber, lock);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new RuntimeException("Unable to run in expected configuration");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.subscriptionManager = new SubscriptionManager(numberOfThreads, subscriber, lock);
|
this.subscriptionManager = new SubscriptionManager(numberOfThreads, subscriber, lock);
|
||||||
@ -161,7 +149,7 @@ class MessageBus implements IMessageBus {
|
|||||||
// setup the work handlers
|
// setup the work handlers
|
||||||
handlers = new MessageHandler[numberOfThreads];
|
handlers = new MessageHandler[numberOfThreads];
|
||||||
for (int i = 0; i < handlers.length; i++) {
|
for (int i = 0; i < handlers.length; i++) {
|
||||||
handlers[i] = new MessageHandler(subscriptionPublisher); // exactly one per thread is used
|
handlers[i] = new MessageHandler(publisher); // exactly one per thread is used
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -336,25 +324,25 @@ class MessageBus implements IMessageBus {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Object message) {
|
void publish(final Object message) {
|
||||||
subscriptionPublisher.publish(message);
|
publisher.publish(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Object message1, final Object message2) {
|
void publish(final Object message1, final Object message2) {
|
||||||
subscriptionPublisher.publish(message1, message2);
|
publisher.publish(message1, message2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Object message1, final Object message2, final Object message3) {
|
void publish(final Object message1, final Object message2, final Object message3) {
|
||||||
subscriptionPublisher.publish(message1, message2, message3);
|
publisher.publish(message1, message2, message3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void publish(final Object[] messages) {
|
void publish(final Object[] messages) {
|
||||||
subscriptionPublisher.publish(messages);
|
publisher.publish(messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -67,10 +67,10 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
class MessageHandler {
|
class MessageHandler {
|
||||||
|
|
||||||
// publish all listeners defined by the given class (includes
|
// publish all listeners defined by the given class (includes
|
||||||
// listeners defined in super classes)
|
// listeners defined in super classes)
|
||||||
public static
|
public static
|
||||||
|
//cache this?
|
||||||
MessageHandler[] get(final Class<?> target) {
|
MessageHandler[] get(final Class<?> target) {
|
||||||
|
|
||||||
// publish all handlers (this will include all (inherited) methods directly annotated using @Handler)
|
// publish all handlers (this will include all (inherited) methods directly annotated using @Handler)
|
||||||
|
@ -24,13 +24,13 @@ import dorkbox.util.messagebus.subscription.Subscription;
|
|||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
@SuppressWarnings("Duplicates")
|
||||||
public
|
public
|
||||||
class PublisherExact_MultiArg implements Publisher {
|
class PublisherExact implements Publisher {
|
||||||
private final ErrorHandlingSupport errorHandler;
|
private final ErrorHandlingSupport errorHandler;
|
||||||
private final Subscriber subscriber;
|
private final Subscriber subscriber;
|
||||||
private final StampedLock lock;
|
private final StampedLock lock;
|
||||||
|
|
||||||
public
|
public
|
||||||
PublisherExact_MultiArg(final ErrorHandlingSupport errorHandler, final Subscriber subscriber, final StampedLock lock) {
|
PublisherExact(final ErrorHandlingSupport errorHandler, final Subscriber subscriber, final StampedLock lock) {
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
this.subscriber = subscriber;
|
this.subscriber = subscriber;
|
||||||
this.lock = lock;
|
this.lock = lock;
|
@ -24,13 +24,13 @@ import dorkbox.util.messagebus.subscription.Subscription;
|
|||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
@SuppressWarnings("Duplicates")
|
||||||
public
|
public
|
||||||
class PublisherExactWithSuperTypes_MultiArg implements Publisher {
|
class PublisherExactWithSuperTypes implements Publisher {
|
||||||
private final ErrorHandlingSupport errorHandler;
|
private final ErrorHandlingSupport errorHandler;
|
||||||
private final Subscriber subscriber;
|
private final Subscriber subscriber;
|
||||||
private final StampedLock lock;
|
private final StampedLock lock;
|
||||||
|
|
||||||
public
|
public
|
||||||
PublisherExactWithSuperTypes_MultiArg(final ErrorHandlingSupport errorHandler, final Subscriber subscriber, final StampedLock lock) {
|
PublisherExactWithSuperTypes(final ErrorHandlingSupport errorHandler, final Subscriber subscriber, final StampedLock lock) {
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
this.subscriber = subscriber;
|
this.subscriber = subscriber;
|
||||||
this.lock = lock;
|
this.lock = lock;
|
@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
@SuppressWarnings("Duplicates")
|
@SuppressWarnings("Duplicates")
|
||||||
public
|
public
|
||||||
class PublisherAll_MultiArg implements Publisher {
|
class PublisherExactWithSuperTypesAndVarity implements Publisher {
|
||||||
private final ErrorHandlingSupport errorHandler;
|
private final ErrorHandlingSupport errorHandler;
|
||||||
|
|
||||||
private final Subscriber subscriber;
|
private final Subscriber subscriber;
|
||||||
@ -38,7 +38,7 @@ class PublisherAll_MultiArg implements Publisher {
|
|||||||
final VarArgUtils varArgUtils;
|
final VarArgUtils varArgUtils;
|
||||||
|
|
||||||
public
|
public
|
||||||
PublisherAll_MultiArg(final ErrorHandlingSupport errorHandler, final Subscriber subscriber, final StampedLock lock) {
|
PublisherExactWithSuperTypesAndVarity(final ErrorHandlingSupport errorHandler, final Subscriber subscriber, final StampedLock lock) {
|
||||||
this.errorHandler = errorHandler;
|
this.errorHandler = errorHandler;
|
||||||
this.subscriber = subscriber;
|
this.subscriber = subscriber;
|
||||||
this.lock = lock;
|
this.lock = lock;
|
||||||
@ -82,7 +82,7 @@ class PublisherAll_MultiArg implements Publisher {
|
|||||||
int length = varArgSubs.length;
|
int length = varArgSubs.length;
|
||||||
Object[] asArray = null;
|
Object[] asArray = null;
|
||||||
|
|
||||||
if (length > 1) {
|
if (length > 0) {
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
|
|
||||||
asArray = (Object[]) Array.newInstance(messageClass, 1);
|
asArray = (Object[]) Array.newInstance(messageClass, 1);
|
||||||
@ -103,7 +103,7 @@ class PublisherAll_MultiArg implements Publisher {
|
|||||||
|
|
||||||
length = varArgSuperSubs.length;
|
length = varArgSuperSubs.length;
|
||||||
|
|
||||||
if (length > 1) {
|
if (length > 0) {
|
||||||
hasSubs = true;
|
hasSubs = true;
|
||||||
|
|
||||||
if (asArray == null) {
|
if (asArray == null) {
|
@ -1,340 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2015 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.messagebus.subscription;
|
|
||||||
|
|
||||||
import dorkbox.util.messagebus.common.HashMapTree;
|
|
||||||
import dorkbox.util.messagebus.common.MessageHandler;
|
|
||||||
import dorkbox.util.messagebus.common.adapter.JavaVersionAdapter;
|
|
||||||
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
|
||||||
import dorkbox.util.messagebus.utils.ClassUtils;
|
|
||||||
import dorkbox.util.messagebus.utils.SubscriptionUtils;
|
|
||||||
import dorkbox.util.messagebus.utils.VarArgUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Permits subscriptions with a varying length of parameters as the signature, which must be match by the publisher for it to be accepted
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
class MultiArgSubscriber implements Subscriber {
|
|
||||||
|
|
||||||
private final ErrorHandlingSupport errorHandler;
|
|
||||||
|
|
||||||
private final SubscriptionUtils subUtils;
|
|
||||||
private final VarArgUtils varArgUtils;
|
|
||||||
|
|
||||||
// all subscriptions per message type. We perpetually KEEP the types, as this lowers the amount of locking required
|
|
||||||
// this is the primary list for dispatching a specific message
|
|
||||||
// write access is synchronized and happens only when a listener of a specific class is registered the first time
|
|
||||||
private final Map<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageSingle;
|
|
||||||
private final HashMapTree<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageMulti;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
public
|
|
||||||
MultiArgSubscriber(final ErrorHandlingSupport errorHandler, final ClassUtils classUtils) {
|
|
||||||
this.errorHandler = errorHandler;
|
|
||||||
|
|
||||||
this.subscriptionsPerMessageSingle = JavaVersionAdapter.concurrentMap(32, LOAD_FACTOR, 1);
|
|
||||||
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>(4, LOAD_FACTOR);
|
|
||||||
|
|
||||||
this.subUtils = new SubscriptionUtils(classUtils, Subscriber.LOAD_FACTOR);
|
|
||||||
|
|
||||||
// var arg subscriptions keep track of which subscriptions can handle varArgs. SUB/UNSUB dumps it, so it is recreated dynamically.
|
|
||||||
// it's a hit on SUB/UNSUB, but improves performance of handlers
|
|
||||||
this.varArgUtils = new VarArgUtils(classUtils, Subscriber.LOAD_FACTOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
AtomicBoolean getVarArgPossibility() {
|
|
||||||
return varArgPossibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
VarArgUtils getVarArgUtils() {
|
|
||||||
return varArgUtils;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void clear() {
|
|
||||||
this.subUtils.clear();
|
|
||||||
this.varArgUtils.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<?>, ArrayList<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: {
|
|
||||||
errorHandler.handleError("Error while trying to subscribe class", listenerClass);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
ArrayList<Subscription> subs = subsPerMessageSingle.get(type0);
|
|
||||||
if (subs == null) {
|
|
||||||
subs = new ArrayList<Subscription>();
|
|
||||||
|
|
||||||
// is this handler able to accept var args?
|
|
||||||
if (handler.getVarArgClass() != null) {
|
|
||||||
varArgPossibility.lazySet(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
subsPerMessageSingle.put(type0, subs);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void register(final Class<?> listenerClass, final int handlersSize, final Subscription[] subsPerListener) {
|
|
||||||
|
|
||||||
final Map<Class<?>, ArrayList<Subscription>> subsPerMessageSingle = this.subscriptionsPerMessageSingle;
|
|
||||||
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti = this.subscriptionsPerMessageMulti;
|
|
||||||
final AtomicBoolean varArgPossibility = this.varArgPossibility;
|
|
||||||
|
|
||||||
Subscription subscription;
|
|
||||||
|
|
||||||
for (int i = 0; i < handlersSize; i++) {
|
|
||||||
subscription = subsPerListener[i];
|
|
||||||
|
|
||||||
// activate this subscription for publication
|
|
||||||
// now add this subscription to each of the handled types
|
|
||||||
registerMulti(subscription, listenerClass, subsPerMessageSingle, subsPerMessageMulti, varArgPossibility);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void shutdown() {
|
|
||||||
this.subscriptionsPerMessageSingle.clear();
|
|
||||||
this.subscriptionsPerMessageMulti.clear();
|
|
||||||
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
ArrayList<Subscription> getExactAsArray(final Class<?> messageClass) {
|
|
||||||
return subscriptionsPerMessageSingle.get(messageClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
ArrayList<Subscription> getExactAsArray(final Class<?> messageClass1, final Class<?> messageClass2) {
|
|
||||||
return subscriptionsPerMessageMulti.get(messageClass1, messageClass2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
ArrayList<Subscription> getExactAsArray(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
|
||||||
return subscriptionsPerMessageMulti.get(messageClass1, messageClass2, messageClass3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Subscription[] getExact(final Class<?> messageClass) {
|
|
||||||
final ArrayList<Subscription> collection = getExactAsArray(messageClass);
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
final Subscription[] subscriptions = new Subscription[collection.size()];
|
|
||||||
collection.toArray(subscriptions);
|
|
||||||
|
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Subscription[] getExact(final Class<?> messageClass1, final Class<?> messageClass2) {
|
|
||||||
final ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2);
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
final Subscription[] subscriptions = new Subscription[collection.size()];
|
|
||||||
collection.toArray(subscriptions);
|
|
||||||
|
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Subscription[] getExact(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
|
||||||
|
|
||||||
final ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3);
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
final Subscription[] subscriptions = new Subscription[collection.size()];
|
|
||||||
collection.toArray(subscriptions);
|
|
||||||
|
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Subscription[] getExactAndSuper(final Class<?> messageClass) {
|
|
||||||
ArrayList<Subscription> collection = getExactAsArray(messageClass); // can return null
|
|
||||||
|
|
||||||
// now publish superClasses
|
|
||||||
final ArrayList<Subscription> superSubscriptions = this.subUtils.getSuperSubscriptions(messageClass, this); // NOT return null
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
collection = new ArrayList<Subscription>(collection);
|
|
||||||
|
|
||||||
if (!superSubscriptions.isEmpty()) {
|
|
||||||
collection.addAll(superSubscriptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!superSubscriptions.isEmpty()) {
|
|
||||||
collection = superSubscriptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
final Subscription[] subscriptions = new Subscription[collection.size()];
|
|
||||||
collection.toArray(subscriptions);
|
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2) {
|
|
||||||
ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2); // can return null
|
|
||||||
|
|
||||||
// now publish superClasses
|
|
||||||
final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2,
|
|
||||||
this); // NOT return null
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
collection = new ArrayList<Subscription>(collection);
|
|
||||||
|
|
||||||
if (!superSubs.isEmpty()) {
|
|
||||||
collection.addAll(superSubs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!superSubs.isEmpty()) {
|
|
||||||
collection = superSubs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
final Subscription[] subscriptions = new Subscription[collection.size()];
|
|
||||||
collection.toArray(subscriptions);
|
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// can return null
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
|
||||||
|
|
||||||
ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3); // can return null
|
|
||||||
|
|
||||||
// now publish superClasses
|
|
||||||
final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2, messageClass3,
|
|
||||||
this); // NOT return null
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
collection = new ArrayList<Subscription>(collection);
|
|
||||||
|
|
||||||
if (!superSubs.isEmpty()) {
|
|
||||||
collection.addAll(superSubs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!superSubs.isEmpty()) {
|
|
||||||
collection = superSubs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collection != null) {
|
|
||||||
final Subscription[] subscriptions = new Subscription[collection.size()];
|
|
||||||
collection.toArray(subscriptions);
|
|
||||||
return subscriptions;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,42 +15,325 @@
|
|||||||
*/
|
*/
|
||||||
package dorkbox.util.messagebus.subscription;
|
package dorkbox.util.messagebus.subscription;
|
||||||
|
|
||||||
|
import dorkbox.util.messagebus.common.HashMapTree;
|
||||||
|
import dorkbox.util.messagebus.common.MessageHandler;
|
||||||
|
import dorkbox.util.messagebus.common.adapter.JavaVersionAdapter;
|
||||||
|
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
||||||
|
import dorkbox.util.messagebus.utils.ClassUtils;
|
||||||
|
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.Map;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permits subscriptions with a varying length of parameters as the signature, which must be match by the publisher for it to be accepted
|
||||||
|
*/
|
||||||
public
|
public
|
||||||
interface Subscriber {
|
class Subscriber {
|
||||||
float LOAD_FACTOR = 0.8F;
|
public static final float LOAD_FACTOR = 0.8F;
|
||||||
|
|
||||||
AtomicBoolean getVarArgPossibility();
|
private final ErrorHandlingSupport errorHandler;
|
||||||
|
|
||||||
VarArgUtils getVarArgUtils();
|
private final SubscriptionUtils subUtils;
|
||||||
|
private final VarArgUtils varArgUtils;
|
||||||
|
|
||||||
void register(Class<?> listenerClass, int handlersSize, Subscription[] subsPerListener);
|
// all subscriptions per message type. We perpetually KEEP the types, as this lowers the amount of locking required
|
||||||
|
// this is the primary list for dispatching a specific message
|
||||||
|
// write access is synchronized and happens only when a listener of a specific class is registered the first time
|
||||||
|
private final Map<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageSingle;
|
||||||
|
private final HashMapTree<Class<?>, ArrayList<Subscription>> subscriptionsPerMessageMulti;
|
||||||
|
|
||||||
void shutdown();
|
// 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);
|
||||||
|
|
||||||
void clear();
|
private ThreadLocal<ArrayList<Subscription>> listCache = new ThreadLocal<ArrayList<Subscription>>() {
|
||||||
|
@Override
|
||||||
ArrayList<Subscription> getExactAsArray(Class<?> superClass);
|
protected
|
||||||
|
ArrayList<Subscription> initialValue() {
|
||||||
ArrayList<Subscription> getExactAsArray(Class<?> superClass1, Class<?> superClass2);
|
return new ArrayList<Subscription>(8);
|
||||||
|
}
|
||||||
ArrayList<Subscription> getExactAsArray(Class<?> superClass1, Class<?> superClass2, Class<?> superClass3);
|
};
|
||||||
|
|
||||||
|
|
||||||
Subscription[] getExact(Class<?> deadMessageClass);
|
|
||||||
|
|
||||||
Subscription[] getExact(Class<?> messageClass1, Class<?> messageClass2);
|
public
|
||||||
|
Subscriber(final ErrorHandlingSupport errorHandler, final ClassUtils classUtils) {
|
||||||
|
this.errorHandler = errorHandler;
|
||||||
|
|
||||||
Subscription[] getExact(Class<?> messageClass1, Class<?> messageClass2, Class<?> messageClass3);
|
this.subscriptionsPerMessageSingle = JavaVersionAdapter.concurrentMap(32, LOAD_FACTOR, 1);
|
||||||
|
this.subscriptionsPerMessageMulti = new HashMapTree<Class<?>, ArrayList<Subscription>>(4, LOAD_FACTOR);
|
||||||
|
|
||||||
|
this.subUtils = new SubscriptionUtils(classUtils, LOAD_FACTOR);
|
||||||
|
|
||||||
Subscription[] getExactAndSuper(Class<?> messageClass);
|
// var arg subscriptions keep track of which subscriptions can handle varArgs. SUB/UNSUB dumps it, so it is recreated dynamically.
|
||||||
|
// it's a hit on SUB/UNSUB, but improves performance of handlers
|
||||||
|
this.varArgUtils = new VarArgUtils(classUtils, LOAD_FACTOR);
|
||||||
|
}
|
||||||
|
|
||||||
Subscription[] getExactAndSuper(Class<?> messageClass1, Class<?> messageClass2);
|
public
|
||||||
|
AtomicBoolean getVarArgPossibility() {
|
||||||
|
return varArgPossibility;
|
||||||
|
}
|
||||||
|
|
||||||
Subscription[] getExactAndSuper(Class<?> messageClass1, Class<?> messageClass2, Class<?> messageClass3);
|
public
|
||||||
|
VarArgUtils getVarArgUtils() {
|
||||||
|
return varArgUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
void clear() {
|
||||||
|
this.subUtils.clear();
|
||||||
|
this.varArgUtils.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<?>, ArrayList<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 ArrayList<Subscription> cachedSubs = listCache.get();
|
||||||
|
ArrayList<Subscription> subs = subsPerMessageSingle.putIfAbsent(type0, cachedSubs);
|
||||||
|
if (subs == null) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
void register(final Class<?> listenerClass, final int handlersSize, final Subscription[] subsPerListener) {
|
||||||
|
|
||||||
|
final Map<Class<?>, ArrayList<Subscription>> subsPerMessageSingle = this.subscriptionsPerMessageSingle;
|
||||||
|
final HashMapTree<Class<?>, ArrayList<Subscription>> subsPerMessageMulti = this.subscriptionsPerMessageMulti;
|
||||||
|
final AtomicBoolean varArgPossibility = this.varArgPossibility;
|
||||||
|
|
||||||
|
Subscription subscription;
|
||||||
|
|
||||||
|
for (int i = 0; i < handlersSize; i++) {
|
||||||
|
subscription = subsPerListener[i];
|
||||||
|
|
||||||
|
// activate this subscription for publication
|
||||||
|
// now add this subscription to each of the handled types
|
||||||
|
registerMulti(subscription, listenerClass, subsPerMessageSingle, subsPerMessageMulti, varArgPossibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
void shutdown() {
|
||||||
|
this.subscriptionsPerMessageSingle.clear();
|
||||||
|
this.subscriptionsPerMessageMulti.clear();
|
||||||
|
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
ArrayList<Subscription> getExactAsArray(final Class<?> messageClass) {
|
||||||
|
return subscriptionsPerMessageSingle.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
|
||||||
|
public
|
||||||
|
Subscription[] getExact(final Class<?> messageClass) {
|
||||||
|
final ArrayList<Subscription> collection = getExactAsArray(messageClass);
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
final Subscription[] subscriptions = new Subscription[collection.size()];
|
||||||
|
collection.toArray(subscriptions);
|
||||||
|
|
||||||
|
return subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getExact(final Class<?> messageClass1, final Class<?> messageClass2) {
|
||||||
|
final ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2);
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
final Subscription[] subscriptions = new Subscription[collection.size()];
|
||||||
|
collection.toArray(subscriptions);
|
||||||
|
|
||||||
|
return subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getExact(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
|
|
||||||
|
final ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3);
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
final Subscription[] subscriptions = new Subscription[collection.size()];
|
||||||
|
collection.toArray(subscriptions);
|
||||||
|
|
||||||
|
return subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getExactAndSuper(final Class<?> messageClass) {
|
||||||
|
ArrayList<Subscription> collection = getExactAsArray(messageClass); // can return null
|
||||||
|
|
||||||
|
// now publish superClasses
|
||||||
|
final ArrayList<Subscription> superSubscriptions = this.subUtils.getSuperSubscriptions(messageClass, this); // NOT return null
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
collection = new ArrayList<Subscription>(collection);
|
||||||
|
|
||||||
|
if (!superSubscriptions.isEmpty()) {
|
||||||
|
collection.addAll(superSubscriptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!superSubscriptions.isEmpty()) {
|
||||||
|
collection = superSubscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
final Subscription[] subscriptions = new Subscription[collection.size()];
|
||||||
|
collection.toArray(subscriptions);
|
||||||
|
return subscriptions;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2) {
|
||||||
|
ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2); // can return null
|
||||||
|
|
||||||
|
// now publish superClasses
|
||||||
|
final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2,
|
||||||
|
this); // NOT return null
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
collection = new ArrayList<Subscription>(collection);
|
||||||
|
|
||||||
|
if (!superSubs.isEmpty()) {
|
||||||
|
collection.addAll(superSubs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!superSubs.isEmpty()) {
|
||||||
|
collection = superSubs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
final Subscription[] subscriptions = new Subscription[collection.size()];
|
||||||
|
collection.toArray(subscriptions);
|
||||||
|
return subscriptions;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// can return null
|
||||||
|
public
|
||||||
|
Subscription[] getExactAndSuper(final Class<?> messageClass1, final Class<?> messageClass2, final Class<?> messageClass3) {
|
||||||
|
|
||||||
|
ArrayList<Subscription> collection = getExactAsArray(messageClass1, messageClass2, messageClass3); // can return null
|
||||||
|
|
||||||
|
// now publish superClasses
|
||||||
|
final ArrayList<Subscription> superSubs = this.subUtils.getSuperSubscriptions(messageClass1, messageClass2, messageClass3,
|
||||||
|
this); // NOT return null
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
collection = new ArrayList<Subscription>(collection);
|
||||||
|
|
||||||
|
if (!superSubs.isEmpty()) {
|
||||||
|
collection.addAll(superSubs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!superSubs.isEmpty()) {
|
||||||
|
collection = superSubs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collection != null) {
|
||||||
|
final Subscription[] subscriptions = new Subscription[collection.size()];
|
||||||
|
collection.toArray(subscriptions);
|
||||||
|
return subscriptions;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,13 +39,14 @@ package dorkbox.util.messagebus.subscription;
|
|||||||
|
|
||||||
import com.esotericsoftware.reflectasm.MethodAccess;
|
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||||
import dorkbox.util.messagebus.common.MessageHandler;
|
import dorkbox.util.messagebus.common.MessageHandler;
|
||||||
import dorkbox.util.messagebus.common.StrongConcurrentSetV8;
|
|
||||||
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.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,10 +79,20 @@ class Subscription {
|
|||||||
public
|
public
|
||||||
Subscription(final MessageHandler handler, final float loadFactor, final int stripeSize) {
|
Subscription(final MessageHandler handler, final float loadFactor, final int stripeSize) {
|
||||||
this.handlerMetadata = handler;
|
this.handlerMetadata = handler;
|
||||||
this.listeners = new StrongConcurrentSetV8<Object>(16, loadFactor, stripeSize);
|
// this.listeners = new StrongConcurrentSetV8<Object>(16, loadFactor, stripeSize);
|
||||||
|
|
||||||
|
///this is by far, the fastest
|
||||||
|
this.listeners = new ConcurrentSkipListSet<>(new Comparator() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
int compare(final Object o1, final Object o2) {
|
||||||
|
return Integer.compare(o1.hashCode(), o2.hashCode());
|
||||||
|
}
|
||||||
|
});
|
||||||
// this.listeners = new StrongConcurrentSet<Object>(16, 0.85F);
|
// this.listeners = new StrongConcurrentSet<Object>(16, 0.85F);
|
||||||
// this.listeners = new ConcurrentLinkedQueue2<Object>();
|
// this.listeners = new ConcurrentLinkedQueue2<Object>();
|
||||||
// this.listeners = new CopyOnWriteArrayList<Object>();
|
// this.listeners = new CopyOnWriteArrayList<Object>();
|
||||||
|
// this.listeners = new CopyOnWriteArraySet<Object>(); // not very good
|
||||||
|
|
||||||
IHandlerInvocation invocation = new ReflectiveHandlerInvocation();
|
IHandlerInvocation invocation = new ReflectiveHandlerInvocation();
|
||||||
if (handler.isSynchronized()) {
|
if (handler.isSynchronized()) {
|
||||||
|
@ -130,7 +130,7 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
// it was still null, so we actually have to create the rest of the subs
|
// it was still null, so we actually have to create the rest of the subs
|
||||||
if (subscriptions == null) {
|
if (subscriptions == null) {
|
||||||
subscriber.register(listenerClass, handlersSize, subsPerListener);
|
subscriber.register(listenerClass, handlersSize, subsPerListener); // this adds to subscriptionsPerMessage
|
||||||
|
|
||||||
subsPerListenerMap.put(listenerClass, subsPerListener);
|
subsPerListenerMap.put(listenerClass, subsPerListener);
|
||||||
lock.unlockWrite(stamp);
|
lock.unlockWrite(stamp);
|
||||||
|
@ -19,7 +19,8 @@ public class MultiMessageTest extends MessageBusTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiMessageSending() {
|
public void testMultiMessageSending() {
|
||||||
IMessageBus bus = new MessageBus();
|
IMessageBus bus = new MessageBus(IMessageBus.PublishMode.ExactWithSuperTypesAndVarity, Runtime.getRuntime().availableProcessors() / 2);
|
||||||
|
// IMessageBus bus = new MessageBus(); // non varity mode
|
||||||
bus.start();
|
bus.start();
|
||||||
|
|
||||||
MultiListener listener1 = new MultiListener();
|
MultiListener listener1 = new MultiListener();
|
||||||
@ -48,6 +49,7 @@ public class MultiMessageTest extends MessageBusTest {
|
|||||||
count.set(0);
|
count.set(0);
|
||||||
|
|
||||||
|
|
||||||
|
// test async publication
|
||||||
bus.publishAsync("s");
|
bus.publishAsync("s");
|
||||||
bus.publishAsync("s", "s");
|
bus.publishAsync("s", "s");
|
||||||
bus.publishAsync("s", "s", "s");
|
bus.publishAsync("s", "s", "s");
|
||||||
@ -58,59 +60,7 @@ public class MultiMessageTest extends MessageBusTest {
|
|||||||
while (bus.hasPendingMessages()) {
|
while (bus.hasPendingMessages()) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(ConcurrentUnits);
|
Thread.sleep(ConcurrentUnits);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException ignored) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(13, count.get());
|
|
||||||
|
|
||||||
|
|
||||||
bus.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFirstArgMultiMessageSending() {
|
|
||||||
IMessageBus bus = new MessageBus(IMessageBus.PublishMode.ExactWithSuperTypes, IMessageBus.SubscribeMode.FirstArg,
|
|
||||||
Runtime.getRuntime().availableProcessors() / 2);
|
|
||||||
bus.start();
|
|
||||||
|
|
||||||
FirstListener listener = new FirstListener();
|
|
||||||
bus.subscribe(listener);
|
|
||||||
bus.unsubscribe(listener);
|
|
||||||
|
|
||||||
bus.publish("s");
|
|
||||||
bus.publish("s", "s");
|
|
||||||
bus.publish("s", "s", "s");
|
|
||||||
bus.publish(1, "s");
|
|
||||||
bus.publish(1, 2, "s");
|
|
||||||
bus.publish(new Integer[] {1, 2, 3, 4, 5, 6});
|
|
||||||
|
|
||||||
assertEquals(0, count.get());
|
|
||||||
|
|
||||||
bus.subscribe(listener);
|
|
||||||
|
|
||||||
bus.publish("s"); // 4
|
|
||||||
bus.publish("s", "s"); // 3
|
|
||||||
bus.publish("s", "s", "s"); // 3
|
|
||||||
bus.publish(1, "s"); // 1
|
|
||||||
bus.publish(1, 2, "s"); // 2
|
|
||||||
bus.publish(new Integer[] {1, 2, 3, 4, 5, 6}); // 2
|
|
||||||
|
|
||||||
assertEquals(15, count.get());
|
|
||||||
count.set(0);
|
|
||||||
|
|
||||||
|
|
||||||
bus.publishAsync("s");
|
|
||||||
bus.publishAsync("s", "s");
|
|
||||||
bus.publishAsync("s", "s", "s");
|
|
||||||
bus.publish(1, "s");
|
|
||||||
bus.publishAsync(1, 2, "s");
|
|
||||||
bus.publishAsync(new Integer[] {1, 2, 3, 4, 5, 6});
|
|
||||||
|
|
||||||
while (bus.hasPendingMessages()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(ConcurrentUnits);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,53 +120,4 @@ public class MultiMessageTest extends MessageBusTest {
|
|||||||
System.err.println("match Object[]");
|
System.err.println("match Object[]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static class FirstListener {
|
|
||||||
@Handler
|
|
||||||
public void handleSync(Object o) {
|
|
||||||
count.getAndIncrement();
|
|
||||||
System.err.println("match Object");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Handler
|
|
||||||
public void handleSync(String o1) {
|
|
||||||
count.getAndIncrement();
|
|
||||||
System.err.println("match String");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Handler
|
|
||||||
public void handleSync(String o1, String o2) {
|
|
||||||
count.getAndIncrement();
|
|
||||||
System.err.println("match String, String");
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Handler
|
|
||||||
// public void handleSync(String o1, String o2, String o3) {
|
|
||||||
// count.getAndIncrement();
|
|
||||||
// System.err.println("match String, String, String");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Handler
|
|
||||||
// public void handleSync(Integer o1, Integer o2, String o3) {
|
|
||||||
// count.getAndIncrement();
|
|
||||||
// System.err.println("match Integer, Integer, String");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Handler(acceptVarargs = true)
|
|
||||||
// public void handleSync(String... o) {
|
|
||||||
// count.getAndIncrement();
|
|
||||||
// System.err.println("match String[]");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Handler
|
|
||||||
// public void handleSync(Integer... o) {
|
|
||||||
// count.getAndIncrement();
|
|
||||||
// System.err.println("match Integer[]");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Handler(acceptVarargs = true)
|
|
||||||
// public void handleSync(Object... o) {
|
|
||||||
// count.getAndIncrement();
|
|
||||||
// System.err.println("match Object[]");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ import dorkbox.util.messagebus.error.DefaultErrorHandler;
|
|||||||
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
import dorkbox.util.messagebus.error.ErrorHandlingSupport;
|
||||||
import dorkbox.util.messagebus.listeners.*;
|
import dorkbox.util.messagebus.listeners.*;
|
||||||
import dorkbox.util.messagebus.messages.*;
|
import dorkbox.util.messagebus.messages.*;
|
||||||
import dorkbox.util.messagebus.subscription.MultiArgSubscriber;
|
|
||||||
import dorkbox.util.messagebus.subscription.Subscriber;
|
import dorkbox.util.messagebus.subscription.Subscriber;
|
||||||
import dorkbox.util.messagebus.subscription.SubscriptionManager;
|
import dorkbox.util.messagebus.subscription.SubscriptionManager;
|
||||||
import dorkbox.util.messagebus.utils.ClassUtils;
|
import dorkbox.util.messagebus.utils.ClassUtils;
|
||||||
@ -160,7 +159,7 @@ public class SubscriptionManagerTest extends AssertSupport {
|
|||||||
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
||||||
final StampedLock lock = new StampedLock();
|
final StampedLock lock = new StampedLock();
|
||||||
final ClassUtils classUtils = new ClassUtils(Subscriber.LOAD_FACTOR);
|
final ClassUtils classUtils = new ClassUtils(Subscriber.LOAD_FACTOR);
|
||||||
final Subscriber subscriber = new MultiArgSubscriber(errorHandler, classUtils);
|
final Subscriber subscriber = new Subscriber(errorHandler, classUtils);
|
||||||
|
|
||||||
SubscriptionManager subscriptionManager = new SubscriptionManager(1, subscriber, lock);
|
SubscriptionManager subscriptionManager = new SubscriptionManager(1, subscriber, lock);
|
||||||
ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), 1);
|
ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), 1);
|
||||||
@ -184,7 +183,7 @@ public class SubscriptionManagerTest extends AssertSupport {
|
|||||||
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
final ErrorHandlingSupport errorHandler = new DefaultErrorHandler();
|
||||||
final StampedLock lock = new StampedLock();
|
final StampedLock lock = new StampedLock();
|
||||||
final ClassUtils classUtils = new ClassUtils(Subscriber.LOAD_FACTOR);
|
final ClassUtils classUtils = new ClassUtils(Subscriber.LOAD_FACTOR);
|
||||||
final Subscriber subscriber = new MultiArgSubscriber(errorHandler, classUtils);
|
final Subscriber subscriber = new Subscriber(errorHandler, classUtils);
|
||||||
|
|
||||||
final SubscriptionManager subscriptionManager = new SubscriptionManager(1, subscriber, lock);
|
final SubscriptionManager subscriptionManager = new SubscriptionManager(1, subscriber, lock);
|
||||||
|
|
||||||
|
@ -25,7 +25,13 @@ package dorkbox.util.messagebus.common;
|
|||||||
import dorkbox.util.messagebus.subscription.Subscriber;
|
import dorkbox.util.messagebus.subscription.Subscriber;
|
||||||
import dorkbox.util.messagebus.subscription.Subscription;
|
import dorkbox.util.messagebus.subscription.Subscription;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author bennidi
|
* @author bennidi
|
||||||
|
Loading…
Reference in New Issue
Block a user