bugfix issue #3, added more test cases
This commit is contained in:
parent
271a445a97
commit
989ed67285
@ -4,10 +4,14 @@ Mbassador
|
|||||||
Mbassador is a very light-weight message (event) bus implementation following the publish subscribe pattern. It is designed
|
Mbassador is a very light-weight message (event) bus implementation following the publish subscribe pattern. It is designed
|
||||||
for ease of use and aims to be feature rich, extensible while preserving resource efficiency and performance.
|
for ease of use and aims to be feature rich, extensible while preserving resource efficiency and performance.
|
||||||
|
|
||||||
|
It uses a specialized data structure to allow high throughput for concurrent access.
|
||||||
|
|
||||||
Read this documentation to get an overview of its features and how cool this message (event) bus actually is.
|
Read this documentation to get an overview of its features and how cool this message (event) bus actually is.
|
||||||
You can also check out the <a href="http://codeblock.engio.net/?p=37" target="_blank">performance comparison</a>
|
You can also check out the <a href="http://codeblock.engio.net/?p=37" target="_blank">performance comparison</a>
|
||||||
which also contains a partial list of the features of the compared implementations.
|
which also contains a partial list of the features of the compared implementations.
|
||||||
|
|
||||||
|
The current version is 1.0.1.RC
|
||||||
|
|
||||||
Table of contents:
|
Table of contents:
|
||||||
+ [Features](#features)
|
+ [Features](#features)
|
||||||
+ [Usage](#usage)
|
+ [Usage](#usage)
|
||||||
|
2
pom.xml
2
pom.xml
@ -4,7 +4,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.mbassy</groupId>
|
<groupId>org.mbassy</groupId>
|
||||||
<artifactId>mbassador</artifactId>
|
<artifactId>mbassador</artifactId>
|
||||||
<version>1.0.0.RC</version>
|
<version>1.0.1.RC</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>mbassador</name>
|
<name>mbassador</name>
|
||||||
<description>Mbassador is a fast and flexible message bus system that follows the publish subscribe pattern
|
<description>Mbassador is a fast and flexible message bus system that follows the publish subscribe pattern
|
||||||
|
@ -12,23 +12,18 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for all message bus implementations.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* @param <P>
|
||||||
|
*/
|
||||||
public abstract class AbstractMessageBus<T, P extends IMessageBus.IPostCommand> implements IMessageBus<T, P> {
|
public abstract class AbstractMessageBus<T, P extends IMessageBus.IPostCommand> implements IMessageBus<T, P> {
|
||||||
|
|
||||||
|
|
||||||
// This predicate is used to find all message listeners (methods annotated with @Listener)
|
|
||||||
private static final IPredicate<Method> AllMessageListeners = new IPredicate<Method>() {
|
|
||||||
@Override
|
|
||||||
public boolean apply(Method target) {
|
|
||||||
return target.getAnnotation(Listener.class) != null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// executor for asynchronous listeners using unbound queuing strategy to ensure that no events get lost
|
// executor for asynchronous listeners using unbound queuing strategy to ensure that no events get lost
|
||||||
private ExecutorService executor;
|
private ExecutorService executor;
|
||||||
|
|
||||||
|
// the metadata reader that is used to parse objects passed to the subscribe method
|
||||||
private MetadataReader metadataReader = new MetadataReader();
|
private MetadataReader metadataReader = new MetadataReader();
|
||||||
|
|
||||||
// all subscriptions per message type
|
// all subscriptions per message type
|
||||||
@ -64,7 +59,7 @@ public abstract class AbstractMessageBus<T, P extends IMessageBus.IPostCommand>
|
|||||||
public void run() {
|
public void run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
pendingMessages.take().execute();
|
pendingMessages.take().execute();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
handlePublicationError(new PublicationError(e, "Asynchronous publication interrupted", null, null, null));
|
handlePublicationError(new PublicationError(e, "Asynchronous publication interrupted", null, null, null));
|
||||||
return;
|
return;
|
||||||
@ -95,20 +90,23 @@ public abstract class AbstractMessageBus<T, P extends IMessageBus.IPostCommand>
|
|||||||
|
|
||||||
protected abstract SubscriptionFactory getSubscriptionFactory();
|
protected abstract SubscriptionFactory getSubscriptionFactory();
|
||||||
|
|
||||||
protected void initialize(){}
|
protected void initialize() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<IPublicationErrorHandler> getRegisteredErrorHandlers() {
|
public Collection<IPublicationErrorHandler> getRegisteredErrorHandlers() {
|
||||||
return Collections.unmodifiableCollection(errorHandlers);
|
return Collections.unmodifiableCollection(errorHandlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsubscribe(Object listener) {
|
public boolean unsubscribe(Object listener) {
|
||||||
if (listener == null) return;
|
if (listener == null) return false;
|
||||||
Collection<Subscription> subscriptions = subscriptionsPerListener.get(listener.getClass());
|
Collection<Subscription> subscriptions = subscriptionsPerListener.get(listener.getClass());
|
||||||
if (subscriptions == null) return;
|
if (subscriptions == null) return false;
|
||||||
|
boolean isRemoved = false;
|
||||||
for (Subscription subscription : subscriptions) {
|
for (Subscription subscription : subscriptions) {
|
||||||
subscription.unsubscribe(listener);
|
isRemoved = isRemoved || subscription.unsubscribe(listener);
|
||||||
}
|
}
|
||||||
|
return isRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -122,7 +120,7 @@ public abstract class AbstractMessageBus<T, P extends IMessageBus.IPostCommand>
|
|||||||
synchronized (this) { // new subscriptions must be processed sequentially for each class
|
synchronized (this) { // new subscriptions must be processed sequentially for each class
|
||||||
subscriptionsByListener = subscriptionsPerListener.get(listeningClass);
|
subscriptionsByListener = subscriptionsPerListener.get(listeningClass);
|
||||||
if (subscriptionsByListener == null) { // double check (a bit ugly but works here)
|
if (subscriptionsByListener == null) { // double check (a bit ugly but works here)
|
||||||
List<Method> messageHandlers = getListeners(listeningClass); // get all methods with subscriptions
|
List<Method> messageHandlers = metadataReader.getListeners(listeningClass); // get all methods with subscriptions
|
||||||
subscriptionsByListener = new ArrayList<Subscription>(messageHandlers.size()); // it's safe to use non-concurrent collection here (read only)
|
subscriptionsByListener = new ArrayList<Subscription>(messageHandlers.size()); // it's safe to use non-concurrent collection here (read only)
|
||||||
if (messageHandlers.isEmpty()) { // remember the class as non listening class
|
if (messageHandlers.isEmpty()) { // remember the class as non listening class
|
||||||
nonListeners.add(listeningClass);
|
nonListeners.add(listeningClass);
|
||||||
@ -143,7 +141,9 @@ public abstract class AbstractMessageBus<T, P extends IMessageBus.IPostCommand>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// register the listener to the existing subscriptions
|
// register the listener to the existing subscriptions
|
||||||
for (Subscription sub : subscriptionsByListener) sub.subscribe(listener);
|
for (Subscription sub : subscriptionsByListener){
|
||||||
|
sub.subscribe(listener);
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -209,16 +209,12 @@ public abstract class AbstractMessageBus<T, P extends IMessageBus.IPostCommand>
|
|||||||
return listener.getParameterTypes()[0];
|
return listener.getParameterTypes()[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all listeners defined by the given class (includes
|
|
||||||
// listeners defined in super classes)
|
|
||||||
private static List<Method> getListeners(Class<?> target) {
|
|
||||||
return ReflectionUtils.getMethods(AllMessageListeners, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void handlePublicationError(PublicationError error) {
|
public void handlePublicationError(PublicationError error) {
|
||||||
for (IPublicationErrorHandler errorHandler : errorHandlers)
|
for (IPublicationErrorHandler errorHandler : errorHandlers){
|
||||||
errorHandler.handleError(error);
|
errorHandler.handleError(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,8 +62,10 @@ public interface IMessageBus<T, P extends IMessageBus.IPostCommand> {
|
|||||||
* will not have any effect.
|
* will not have any effect.
|
||||||
*
|
*
|
||||||
* @param listener
|
* @param listener
|
||||||
|
* @return true, if the listener was found and successfully removed
|
||||||
|
* false otherwise
|
||||||
*/
|
*/
|
||||||
public void unsubscribe(Object listener);
|
public boolean unsubscribe(Object listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -3,9 +3,8 @@ package org.mbassy;
|
|||||||
/**
|
/**
|
||||||
* TODO. Insert class description here
|
* TODO. Insert class description here
|
||||||
* <p/>
|
* <p/>
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 2/22/12
|
* Date: 2/22/12
|
||||||
* Time: 5:03 PM
|
|
||||||
*/
|
*/
|
||||||
public interface IPublicationErrorHandler {
|
public interface IPublicationErrorHandler {
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import java.lang.reflect.Method;
|
|||||||
* Publication errors are created when object publication fails for some reason and contain details
|
* Publication errors are created when object publication fails for some reason and contain details
|
||||||
* as to the cause and location where they occured.
|
* as to the cause and location where they occured.
|
||||||
* <p/>
|
* <p/>
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 2/22/12
|
* Date: 2/22/12
|
||||||
* Time: 4:59 PM
|
* Time: 4:59 PM
|
||||||
*/
|
*/
|
||||||
|
@ -2,7 +2,7 @@ package org.mbassy;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/12/12
|
* Date: 11/12/12
|
||||||
* Time: 8:44 PM
|
* Time: 8:44 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -66,11 +66,11 @@ public class ConcurrentSet<T> implements Iterable<T> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConcurrentSet<T> remove(T element) {
|
public boolean remove(T element) {
|
||||||
if (!entries.containsKey(element)) return this;
|
if (!entries.containsKey(element)) return false;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
Entry<T> listelement = entries.get(element);
|
Entry<T> listelement = entries.get(element);
|
||||||
if(listelement == null)return this;
|
if(listelement == null)return false;
|
||||||
if (listelement != head) {
|
if (listelement != head) {
|
||||||
listelement.remove();
|
listelement.remove();
|
||||||
} else {
|
} else {
|
||||||
@ -78,7 +78,7 @@ public class ConcurrentSet<T> implements Iterable<T> {
|
|||||||
}
|
}
|
||||||
entries.remove(element);
|
entries.remove(element);
|
||||||
}
|
}
|
||||||
return this;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterator<T> iterator() {
|
public Iterator<T> iterator() {
|
||||||
|
@ -2,7 +2,7 @@ package org.mbassy.common;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 10/22/12
|
* Date: 10/22/12
|
||||||
* Time: 9:33 AM
|
* Time: 9:33 AM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -1,64 +1,85 @@
|
|||||||
package org.mbassy.common;
|
package org.mbassy.common;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 2/16/12
|
* Date: 2/16/12
|
||||||
* Time: 12:14 PM
|
* Time: 12:14 PM
|
||||||
*/
|
*/
|
||||||
public class ReflectionUtils {
|
public class ReflectionUtils {
|
||||||
|
|
||||||
public static List<Method> getMethods(IPredicate<Method> condition, Class<?> target) {
|
public static List<Method> getMethods(IPredicate<Method> condition, Class<?> target) {
|
||||||
List<Method> methods = new LinkedList<Method>();
|
List<Method> methods = new LinkedList<Method>();
|
||||||
try {
|
try {
|
||||||
for (Method method : target.getDeclaredMethods()) {
|
for (Method method : target.getDeclaredMethods()) {
|
||||||
if (condition.apply(method)) {
|
if (condition.apply(method)) {
|
||||||
methods.add(method);
|
methods.add(method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
//nop
|
//nop
|
||||||
}
|
}
|
||||||
if (!target.equals(Object.class)) {
|
if (!target.equals(Object.class)) {
|
||||||
methods.addAll(getMethods(condition, target.getSuperclass()));
|
methods.addAll(getMethods(condition, target.getSuperclass()));
|
||||||
}
|
}
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Field> getFields(IPredicate<Field> condition, Class<?> target) {
|
public static Method getOverridingMethod(Method overridingMethod, Class subclass) {
|
||||||
List<Field> methods = new LinkedList<Field>();
|
Class current = subclass;
|
||||||
try {
|
while(!current.equals(overridingMethod.getDeclaringClass())){
|
||||||
for (Field method : target.getDeclaredFields()) {
|
try {
|
||||||
if (condition.apply(method)) {
|
Method overridden = current.getDeclaredMethod(overridingMethod.getName(), overridingMethod.getParameterTypes());
|
||||||
methods.add(method);
|
return overridden;
|
||||||
}
|
} catch (NoSuchMethodException e) {
|
||||||
}
|
current = current.getSuperclass();
|
||||||
} catch (Exception e) {
|
}
|
||||||
//nop
|
}
|
||||||
}
|
return null;
|
||||||
if (!target.equals(Object.class)) {
|
}
|
||||||
methods.addAll(getFields(condition, target.getSuperclass()));
|
|
||||||
}
|
|
||||||
return methods;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object callMethod(Object o, final String methodName, Object... args) {
|
public static List<Method> withoutOverridenSuperclassMethods(List<Method> allMethods) {
|
||||||
|
List<Method> filtered = new LinkedList<Method>();
|
||||||
|
for (Method method : allMethods) {
|
||||||
|
if (!containsOverridingMethod(allMethods, method)) filtered.add(method);
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
if(o == null || methodName == null) {
|
public static boolean containsOverridingMethod(List<Method> allMethods, Method methodToCheck) {
|
||||||
return null;
|
for (Method method : allMethods) {
|
||||||
}
|
if (isOverriddenBy(methodToCheck, method)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isOverriddenBy(Method superclassMethod, Method subclassMethod) {
|
||||||
|
// if the declaring classes are the same or the subclass method is not defined in the subbclass
|
||||||
|
// hierarchy of the given superclass method or the method names are not the same then
|
||||||
|
// subclassMethod does not override superclassMethod
|
||||||
|
if (superclassMethod.getDeclaringClass().equals(subclassMethod.getDeclaringClass())
|
||||||
|
|| !superclassMethod.getDeclaringClass().isAssignableFrom(subclassMethod.getDeclaringClass())
|
||||||
|
|| !superclassMethod.getName().equals(subclassMethod.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class[] superClassMethodParameters = superclassMethod.getParameterTypes();
|
||||||
|
Class[] subClassMethodParameters = superclassMethod.getParameterTypes();
|
||||||
|
// method must specify the same number of parameters
|
||||||
|
if(subClassMethodParameters.length != subClassMethodParameters.length){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//the parameters must occur in the exact same order
|
||||||
|
for(int i = 0 ; i< subClassMethodParameters.length; i++){
|
||||||
|
if(!superClassMethodParameters[i].equals(subClassMethodParameters[i])){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Object res = null;
|
|
||||||
try {
|
|
||||||
Method m = o.getClass().getMethod(methodName);
|
|
||||||
res = m.invoke(o, args);
|
|
||||||
} catch (Exception e) {
|
|
||||||
//logger.warn("Not possible to get value", e);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import java.lang.annotation.*;
|
|||||||
/**
|
/**
|
||||||
* TODO. Insert class description here
|
* TODO. Insert class description here
|
||||||
* <p/>
|
* <p/>
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 2/8/12
|
* Date: 2/8/12
|
||||||
* Time: 3:35 PM
|
* Time: 3:35 PM
|
||||||
*/
|
*/
|
||||||
|
@ -4,7 +4,7 @@ package org.mbassy.listener;
|
|||||||
* Object filters can be used to prevent certain messages to be delivered to a specific listener.
|
* Object filters can be used to prevent certain messages to be delivered to a specific listener.
|
||||||
* If a filter is used the message will only be delivered if it passes the filter(s)
|
* If a filter is used the message will only be delivered if it passes the filter(s)
|
||||||
*
|
*
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 2/8/12
|
* Date: 2/8/12
|
||||||
*/
|
*/
|
||||||
public interface MessageFilter {
|
public interface MessageFilter {
|
||||||
|
@ -7,7 +7,7 @@ import org.mbassy.listener.MessageFilter;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
*/
|
*/
|
||||||
public class MessageHandlerMetadata {
|
public class MessageHandlerMetadata {
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
package org.mbassy.listener;
|
package org.mbassy.listener;
|
||||||
|
|
||||||
|
import org.mbassy.common.IPredicate;
|
||||||
|
import org.mbassy.common.ReflectionUtils;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/16/12
|
* Date: 11/16/12
|
||||||
* Time: 10:22 AM
|
* Time: 10:22 AM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
*/
|
*/
|
||||||
public class MetadataReader {
|
public class MetadataReader {
|
||||||
|
|
||||||
|
// This predicate is used to find all message listeners (methods annotated with @Listener)
|
||||||
|
private static final IPredicate<Method> AllMessageHandlers = new IPredicate<Method>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(Method target) {
|
||||||
|
return target.getAnnotation(Listener.class) != null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// cache already created filter instances
|
// cache already created filter instances
|
||||||
private final Map<Class<? extends MessageFilter>, MessageFilter> filterCache = new HashMap<Class<? extends MessageFilter>, MessageFilter>();
|
private final Map<Class<? extends MessageFilter>, MessageFilter> filterCache = new HashMap<Class<? extends MessageFilter>, MessageFilter>();
|
||||||
|
|
||||||
@ -40,4 +54,32 @@ public class MetadataReader {
|
|||||||
MessageFilter[] filter = getFilter(config);
|
MessageFilter[] filter = getFilter(config);
|
||||||
return new MessageHandlerMetadata(messageHandler, filter, config);
|
return new MessageHandlerMetadata(messageHandler, filter, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get all listeners defined by the given class (includes
|
||||||
|
// listeners defined in super classes)
|
||||||
|
public List<Method> getListeners(Class<?> target) {
|
||||||
|
List<Method> allMethods = ReflectionUtils.getMethods(AllMessageHandlers, target);
|
||||||
|
List<Method> handlers = new LinkedList<Method>();
|
||||||
|
for(Method listener : allMethods){
|
||||||
|
Method overriddenHandler = ReflectionUtils.getOverridingMethod(listener, target);
|
||||||
|
|
||||||
|
if(overriddenHandler != null && isHandler(overriddenHandler)){
|
||||||
|
handlers.add(overriddenHandler);
|
||||||
|
}
|
||||||
|
if(overriddenHandler == null){
|
||||||
|
handlers.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ReflectionUtils.withoutOverridenSuperclassMethods(handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isHandler(Method m){
|
||||||
|
Annotation[] annotations = m.getDeclaredAnnotations();
|
||||||
|
for(Annotation annotation : annotations){
|
||||||
|
if(annotation.equals(Listener.class))return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package org.mbassy.listener;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/16/12
|
* Date: 11/16/12
|
||||||
* Time: 10:01 AM
|
* Time: 10:01 AM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -10,7 +10,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
* Time: 3:50 PM
|
* Time: 3:50 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -12,7 +12,7 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
* Time: 3:48 PM
|
* Time: 3:48 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -10,7 +10,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
* Time: 3:49 PM
|
* Time: 3:49 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -10,12 +10,15 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription is a thread safe container for objects that contain message handlers
|
* Subscription is a thread safe container for objects that contain message handlers
|
||||||
*/
|
*/
|
||||||
public abstract class Subscription {
|
public abstract class Subscription {
|
||||||
|
|
||||||
|
private UUID id = UUID.randomUUID();
|
||||||
|
|
||||||
private final Method handler;
|
private final Method handler;
|
||||||
|
|
||||||
protected ConcurrentSet<Object> listeners = new ConcurrentSet<Object>();
|
protected ConcurrentSet<Object> listeners = new ConcurrentSet<Object>();
|
||||||
@ -88,8 +91,8 @@ public abstract class Subscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void unsubscribe(Object existingListener) {
|
public boolean unsubscribe(Object existingListener) {
|
||||||
listeners.remove(existingListener);
|
return listeners.remove(existingListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -97,7 +100,7 @@ public abstract class Subscription {
|
|||||||
@Override
|
@Override
|
||||||
public int compare(Subscription o1, Subscription o2) {
|
public int compare(Subscription o1, Subscription o2) {
|
||||||
int result = o1.getPriority() - o2.getPriority();
|
int result = o1.getPriority() - o2.getPriority();
|
||||||
return result == 0 ? o1.handler.hashCode() - o2.handler.hashCode() : result;
|
return result == 0 ? o1.id.compareTo(o2.id): result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import java.util.Collection;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/16/12
|
* Date: 11/16/12
|
||||||
*/
|
*/
|
||||||
public class SubscriptionDeliveryRequest<T> {
|
public class SubscriptionDeliveryRequest<T> {
|
||||||
|
@ -8,7 +8,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/16/12
|
* Date: 11/16/12
|
||||||
* Time: 10:39 AM
|
* Time: 10:39 AM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -10,7 +10,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
* Time: 3:48 PM
|
* Time: 3:48 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -11,7 +11,7 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
* Time: 3:45 PM
|
* Time: 3:45 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -10,7 +10,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/14/12
|
* Date: 11/14/12
|
||||||
* Time: 3:49 PM
|
* Time: 3:49 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -10,7 +10,7 @@ import java.util.Random;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/12/12
|
* Date: 11/12/12
|
||||||
* Time: 3:02 PM
|
* Time: 3:02 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
|
@ -1,141 +1,147 @@
|
|||||||
package org.mbassy;
|
package org.mbassy;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mbassy.listener.Filter;
|
import org.mbassy.events.SubTestEvent;
|
||||||
import org.mbassy.listener.Listener;
|
import org.mbassy.events.TestEvent;
|
||||||
import org.mbassy.listener.MessageFilter;
|
import org.mbassy.listeners.*;
|
||||||
import org.mbassy.listener.Mode;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test synchronous and asynchronous dispatch in single and multi-threaded scenario.
|
* Test synchronous and asynchronous dispatch in single and multi-threaded scenario.
|
||||||
*
|
*
|
||||||
* @author bennidi
|
* @author bennidi
|
||||||
* Date: 2/8/12
|
* Date: 2/8/12
|
||||||
*/
|
*/
|
||||||
public class MBassadorTest {
|
public class MBassadorTest extends UnitTest {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSubscribe() throws InterruptedException {
|
public void testSubscribeSimple() throws InterruptedException {
|
||||||
|
|
||||||
MBassador bus = new MBassador();
|
MBassador bus = new MBassador();
|
||||||
int listenerCount = 1000;
|
int listenerCount = 1000;
|
||||||
|
|
||||||
|
for (int i = 1; i <= listenerCount; i++) {
|
||||||
|
EventingTestBean listener = new EventingTestBean();
|
||||||
|
NonListeningBean nonListener = new NonListeningBean();
|
||||||
|
bus.subscribe(listener);
|
||||||
|
bus.subscribe(nonListener);
|
||||||
|
assertTrue(bus.unsubscribe(listener));
|
||||||
|
assertFalse(bus.unsubscribe(nonListener));
|
||||||
|
assertFalse(bus.unsubscribe(new EventingTestBean()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscribeConcurrent() throws Exception {
|
||||||
|
|
||||||
|
MBassador bus = new MBassador();
|
||||||
|
ListenerFactory listenerFactory = new ListenerFactory()
|
||||||
|
.create(100, EventingTestBean.class)
|
||||||
|
.create(100, EventingTestBean2.class)
|
||||||
|
.create(100, EventingTestBean3.class)
|
||||||
|
.create(100, Object.class)
|
||||||
|
.create(100, NonListeningBean.class);
|
||||||
|
|
||||||
|
List<Object> listeners = listenerFactory.build();
|
||||||
|
TestUtil.setup(bus, listeners, 10);
|
||||||
|
|
||||||
|
TestEvent event = new TestEvent();
|
||||||
|
SubTestEvent subEvent = new SubTestEvent();
|
||||||
|
|
||||||
|
bus.publish(event);
|
||||||
|
bus.publish(subEvent);
|
||||||
|
|
||||||
|
pause(4000);
|
||||||
|
|
||||||
|
assertEquals(300, event.counter.get());
|
||||||
|
assertEquals(700, subEvent.counter.get());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAsynchronous() throws InterruptedException {
|
||||||
|
|
||||||
|
MBassador bus = new MBassador();
|
||||||
|
int listenerCount = 1000;
|
||||||
|
List<EventingTestBean> persistentReferences = new ArrayList();
|
||||||
|
|
||||||
for (int i = 1; i <= listenerCount; i++) {
|
for (int i = 1; i <= listenerCount; i++) {
|
||||||
EventingTestBean bean = new EventingTestBean();
|
EventingTestBean bean = new EventingTestBean();
|
||||||
|
persistentReferences.add(bean);
|
||||||
bus.subscribe(bean);
|
bus.subscribe(bean);
|
||||||
bus.unsubscribe(new EventingTestBean());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestEvent event = new TestEvent();
|
||||||
|
TestEvent subEvent = new SubTestEvent();
|
||||||
|
|
||||||
|
bus.publishAsync(event);
|
||||||
|
bus.publishAsync(subEvent);
|
||||||
|
|
||||||
|
pause(2000);
|
||||||
|
|
||||||
|
assertTrue(event.counter.get() == 1000);
|
||||||
|
assertTrue(subEvent.counter.get() == 1000 * 2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnSubscribe() throws InterruptedException {
|
public void testSynchronous() throws InterruptedException {
|
||||||
|
|
||||||
MBassador bus = new MBassador();
|
MBassador bus = new MBassador();
|
||||||
int listenerCount = 1000;
|
int listenerCount = 10;
|
||||||
|
List<EventingTestBean> persistentReferences = new ArrayList();
|
||||||
for (int i = 1; i <= listenerCount; i++) {
|
for (int i = 1; i <= listenerCount; i++) {
|
||||||
bus.unsubscribe(new EventingTestBean());
|
|
||||||
|
|
||||||
|
EventingTestBean bean = new EventingTestBean();
|
||||||
|
persistentReferences.add(bean);
|
||||||
|
bus.subscribe(bean);
|
||||||
|
|
||||||
|
TestEvent event = new TestEvent();
|
||||||
|
TestEvent subEvent = new SubTestEvent();
|
||||||
|
|
||||||
|
bus.publish(event);
|
||||||
|
bus.publish(subEvent);
|
||||||
|
|
||||||
|
assertEquals(i, event.counter.get());
|
||||||
|
|
||||||
|
pause(50);
|
||||||
|
|
||||||
|
assertEquals(i * 2, subEvent.counter.get());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAsynchronous() throws InterruptedException {
|
|
||||||
|
|
||||||
MBassador bus = new MBassador();
|
|
||||||
int listenerCount = 1000;
|
|
||||||
List<EventingTestBean> persistentReferences = new ArrayList();
|
|
||||||
|
|
||||||
for (int i = 1; i <= listenerCount; i++) {
|
|
||||||
EventingTestBean bean = new EventingTestBean();
|
|
||||||
persistentReferences.add(bean);
|
|
||||||
bus.subscribe(bean);
|
|
||||||
}
|
|
||||||
|
|
||||||
TestEvent event = new TestEvent();
|
|
||||||
TestEvent subEvent = new SubTestEvent();
|
|
||||||
|
|
||||||
bus.publishAsync(event);
|
|
||||||
bus.publishAsync(subEvent);
|
|
||||||
|
|
||||||
Thread.sleep(2000);
|
|
||||||
|
|
||||||
Assert.assertTrue(event.counter.get() == 1000);
|
|
||||||
Assert.assertTrue(subEvent.counter.get() == 1000 * 2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSynchronous() throws InterruptedException {
|
public void testConcurrentPublication() {
|
||||||
|
|
||||||
MBassador bus = new MBassador();
|
|
||||||
int listenerCount = 100;
|
|
||||||
List<EventingTestBean> persistentReferences = new ArrayList();
|
|
||||||
for (int i = 1; i <= listenerCount; i++) {
|
|
||||||
|
|
||||||
|
|
||||||
EventingTestBean bean = new EventingTestBean();
|
|
||||||
persistentReferences.add(bean);
|
|
||||||
bus.subscribe(bean);
|
|
||||||
|
|
||||||
TestEvent event = new TestEvent();
|
|
||||||
TestEvent subEvent = new SubTestEvent();
|
|
||||||
|
|
||||||
bus.publish(event);
|
|
||||||
bus.publish(subEvent);
|
|
||||||
|
|
||||||
Assert.assertEquals(i, event.counter.get());
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(10);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.assertEquals(i * 2, subEvent.counter.get());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testConcurrentPublication() {
|
|
||||||
final MBassador bus = new MBassador();
|
final MBassador bus = new MBassador();
|
||||||
final int listenerCount = 100;
|
final int listenerCount = 100;
|
||||||
final int concurenny = 20;
|
final int concurrency = 20;
|
||||||
final CopyOnWriteArrayList<TestEvent> testEvents = new CopyOnWriteArrayList<TestEvent>();
|
final CopyOnWriteArrayList<TestEvent> testEvents = new CopyOnWriteArrayList<TestEvent>();
|
||||||
final CopyOnWriteArrayList<SubTestEvent> subtestEvents = new CopyOnWriteArrayList<SubTestEvent>();
|
final CopyOnWriteArrayList<SubTestEvent> subtestEvents = new CopyOnWriteArrayList<SubTestEvent>();
|
||||||
final CopyOnWriteArrayList<EventingTestBean> persistentReferences = new CopyOnWriteArrayList<EventingTestBean>();
|
final CopyOnWriteArrayList<EventingTestBean> persistentReferences = new CopyOnWriteArrayList<EventingTestBean>();
|
||||||
|
|
||||||
ConcurrentExecutor.runConcurrent(new Runnable() {
|
ConcurrentExecutor.runConcurrent(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
long start = System.currentTimeMillis();
|
for (int i = 0; i < listenerCount; i++) {
|
||||||
for (int i = 0; i < listenerCount; i++) {
|
EventingTestBean bean = new EventingTestBean();
|
||||||
EventingTestBean bean = new EventingTestBean();
|
persistentReferences.add(bean);
|
||||||
persistentReferences.add(bean);
|
|
||||||
bus.subscribe(bean);
|
bus.subscribe(bean);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
long end = System.currentTimeMillis();
|
}, concurrency);
|
||||||
System.out.println("MBassador: Creating " + listenerCount + " listeners took " + (end - start) + " ms");
|
|
||||||
}
|
|
||||||
}, concurenny);
|
|
||||||
|
|
||||||
ConcurrentExecutor.runConcurrent(new Runnable() {
|
ConcurrentExecutor.runConcurrent(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
for (int i = 0; i < listenerCount; i++) {
|
for (int i = 0; i < listenerCount; i++) {
|
||||||
TestEvent event = new TestEvent();
|
TestEvent event = new TestEvent();
|
||||||
SubTestEvent subEvent = new SubTestEvent();
|
SubTestEvent subEvent = new SubTestEvent();
|
||||||
@ -145,67 +151,20 @@ public class MBassadorTest {
|
|||||||
bus.publishAsync(event);
|
bus.publishAsync(event);
|
||||||
bus.publish(subEvent);
|
bus.publish(subEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
long end = System.currentTimeMillis();
|
|
||||||
System.out.println("MBassador: Publishing " + 2 * listenerCount + " events took " + (end - start) + " ms");
|
|
||||||
}
|
}
|
||||||
}, concurenny);
|
}, concurrency);
|
||||||
|
|
||||||
try {
|
pause(3000);
|
||||||
Thread.sleep(3000);
|
|
||||||
} catch (InterruptedException e) {
|
for (TestEvent event : testEvents) {
|
||||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
assertEquals(listenerCount * concurrency, event.counter.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
for(TestEvent event : testEvents){
|
for (SubTestEvent event : subtestEvents) {
|
||||||
Assert.assertEquals(listenerCount * concurenny, event.counter.get());
|
assertEquals(listenerCount * concurrency * 2, event.counter.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
for(SubTestEvent event : subtestEvents){
|
}
|
||||||
Assert.assertEquals(listenerCount * concurenny * 2, event.counter.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class TestEvent {
|
|
||||||
|
|
||||||
public AtomicInteger counter = new AtomicInteger();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SubTestEvent extends TestEvent {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class EventingTestBean {
|
|
||||||
|
|
||||||
// every event of type TestEvent or any subtype will be delivered
|
|
||||||
// to this listener
|
|
||||||
@Listener
|
|
||||||
public void handleTestEvent(TestEvent event) {
|
|
||||||
event.counter.incrementAndGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
// this handler will be invoked asynchronously
|
|
||||||
@Listener(priority = 0, dispatch = Mode.Asynchronous)
|
|
||||||
public void handleSubTestEvent(SubTestEvent event) {
|
|
||||||
event.counter.incrementAndGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
// this handler will receive events of type SubTestEvent
|
|
||||||
// or any subtabe and that passes the given filter
|
|
||||||
@Listener(
|
|
||||||
priority = 10,
|
|
||||||
dispatch = Mode.Synchronous,
|
|
||||||
filters = {@Filter(MessageFilter.None.class),@Filter(MessageFilter.All.class)})
|
|
||||||
public void handleFiltered(SubTestEvent event) {
|
|
||||||
event.counter.incrementAndGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
46
src/test/java/org/mbassy/TestUtil.java
Normal file
46
src/test/java/org/mbassy/TestUtil.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package org.mbassy;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Todo: Add javadoc
|
||||||
|
*
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class TestUtil {
|
||||||
|
|
||||||
|
public static void setup(final IMessageBus bus, final List<Object> listeners, int numberOfThreads) {
|
||||||
|
Runnable[] setupUnits = new Runnable[numberOfThreads];
|
||||||
|
int partitionSize;
|
||||||
|
if(listeners.size() >= numberOfThreads){
|
||||||
|
partitionSize = (int)Math.floor(listeners.size() / numberOfThreads);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
partitionSize = 1;
|
||||||
|
numberOfThreads = listeners.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < numberOfThreads; i++){
|
||||||
|
final int partitionStart = i * partitionSize;
|
||||||
|
final int partitionEnd = (i+1 < numberOfThreads)
|
||||||
|
? partitionStart + partitionSize + 1
|
||||||
|
: listeners.size();
|
||||||
|
setupUnits[i] = new Runnable() {
|
||||||
|
|
||||||
|
private List<Object> listenerSubset = listeners.subList(partitionStart, partitionEnd);
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
for(Object listener : listenerSubset){
|
||||||
|
bus.subscribe(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentExecutor.runConcurrent(setupUnits);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
package org.mbassy;
|
package org.mbassy;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created with IntelliJ IDEA.
|
* Created with IntelliJ IDEA.
|
||||||
* User: benni
|
* @author bennidi
|
||||||
* Date: 11/12/12
|
* Date: 11/12/12
|
||||||
* Time: 3:16 PM
|
* Time: 3:16 PM
|
||||||
* To change this template use File | Settings | File Templates.
|
* To change this template use File | Settings | File Templates.
|
||||||
@ -31,4 +33,40 @@ public class UnitTest {
|
|||||||
System.gc();
|
System.gc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void fail(String message) {
|
||||||
|
Assert.fail(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fail() {
|
||||||
|
Assert.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertTrue(Boolean condition) {
|
||||||
|
Assert.assertTrue(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertTrue(String message, Boolean condition) {
|
||||||
|
Assert.assertTrue(message, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertFalse(Boolean condition) {
|
||||||
|
Assert.assertFalse(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertNull(Object object) {
|
||||||
|
Assert.assertNull(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertNotNull(Object object) {
|
||||||
|
Assert.assertNotNull(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertFalse(String message, Boolean condition) {
|
||||||
|
Assert.assertFalse(message, condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertEquals(Object expected, Object actual) {
|
||||||
|
Assert.assertEquals(expected, actual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
10
src/test/java/org/mbassy/events/SubTestEvent.java
Normal file
10
src/test/java/org/mbassy/events/SubTestEvent.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package org.mbassy.events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class SubTestEvent extends TestEvent {
|
||||||
|
|
||||||
|
}
|
15
src/test/java/org/mbassy/events/TestEvent.java
Normal file
15
src/test/java/org/mbassy/events/TestEvent.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package org.mbassy.events;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class TestEvent {
|
||||||
|
|
||||||
|
public AtomicInteger counter = new AtomicInteger();
|
||||||
|
|
||||||
|
}
|
42
src/test/java/org/mbassy/listeners/EventingTestBean.java
Normal file
42
src/test/java/org/mbassy/listeners/EventingTestBean.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package org.mbassy.listeners;
|
||||||
|
|
||||||
|
import org.mbassy.events.SubTestEvent;
|
||||||
|
import org.mbassy.events.TestEvent;
|
||||||
|
import org.mbassy.listener.Filter;
|
||||||
|
import org.mbassy.listener.Listener;
|
||||||
|
import org.mbassy.listener.MessageFilter;
|
||||||
|
import org.mbassy.listener.Mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic bean that defines some event handlers to be used for different unit testting scenarios
|
||||||
|
*
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class EventingTestBean {
|
||||||
|
|
||||||
|
// every event of type TestEvent or any subtype will be delivered
|
||||||
|
// to this listener
|
||||||
|
@Listener
|
||||||
|
public void handleTestEvent(TestEvent event) {
|
||||||
|
event.counter.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this handler will be invoked asynchronously
|
||||||
|
@Listener(priority = 0, dispatch = Mode.Asynchronous)
|
||||||
|
public void handleSubTestEvent(SubTestEvent event) {
|
||||||
|
event.counter.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this handler will receive events of type SubTestEvent
|
||||||
|
// or any subtabe and that passes the given filter
|
||||||
|
@Listener(
|
||||||
|
priority = 10,
|
||||||
|
dispatch = Mode.Synchronous,
|
||||||
|
filters = {@Filter(MessageFilter.None.class), @Filter(MessageFilter.All.class)})
|
||||||
|
public void handleFiltered(SubTestEvent event) {
|
||||||
|
event.counter.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
18
src/test/java/org/mbassy/listeners/EventingTestBean2.java
Normal file
18
src/test/java/org/mbassy/listeners/EventingTestBean2.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package org.mbassy.listeners;
|
||||||
|
|
||||||
|
import org.mbassy.events.SubTestEvent;
|
||||||
|
import org.mbassy.listener.Listener;
|
||||||
|
import org.mbassy.listener.Mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class EventingTestBean2 extends EventingTestBean{
|
||||||
|
|
||||||
|
// redefine the configuration for this handler
|
||||||
|
@Listener(dispatch = Mode.Synchronous)
|
||||||
|
public void handleSubTestEvent(SubTestEvent event) {
|
||||||
|
super.handleSubTestEvent(event);
|
||||||
|
}
|
||||||
|
}
|
20
src/test/java/org/mbassy/listeners/EventingTestBean3.java
Normal file
20
src/test/java/org/mbassy/listeners/EventingTestBean3.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package org.mbassy.listeners;
|
||||||
|
|
||||||
|
import org.mbassy.events.SubTestEvent;
|
||||||
|
import org.mbassy.listener.Listener;
|
||||||
|
import org.mbassy.listener.Mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class EventingTestBean3 extends EventingTestBean2{
|
||||||
|
|
||||||
|
|
||||||
|
// this handler will be invoked asynchronously
|
||||||
|
@Listener(priority = 0, dispatch = Mode.Synchronous)
|
||||||
|
public void handleSubTestEventAgain(SubTestEvent event) {
|
||||||
|
event.counter.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/test/java/org/mbassy/listeners/ListenerFactory.java
Normal file
39
src/test/java/org/mbassy/listeners/ListenerFactory.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package org.mbassy.listeners;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This factory will create a list of beans according to some specified configuration.
|
||||||
|
* It can be used to setup different test scenarios.
|
||||||
|
*
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class ListenerFactory {
|
||||||
|
|
||||||
|
private Map<Class, Integer> requiredBeans = new HashMap<Class, Integer>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ListenerFactory create(int numberOfInstance, Class clazz){
|
||||||
|
requiredBeans.put(clazz, numberOfInstance);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Object> build() throws Exception{
|
||||||
|
List<Object> beans = new LinkedList<Object>();
|
||||||
|
for(Class clazz : requiredBeans.keySet()){
|
||||||
|
int numberOfRequiredBeans = requiredBeans.get(clazz);
|
||||||
|
for(int i = 0; i < numberOfRequiredBeans; i++){
|
||||||
|
beans.add(clazz.newInstance());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return beans;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
32
src/test/java/org/mbassy/listeners/NonListeningBean.java
Normal file
32
src/test/java/org/mbassy/listeners/NonListeningBean.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package org.mbassy.listeners;
|
||||||
|
|
||||||
|
import org.mbassy.events.SubTestEvent;
|
||||||
|
import org.mbassy.events.TestEvent;
|
||||||
|
import org.mbassy.listener.Listener;
|
||||||
|
import org.mbassy.listener.Mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bean overrides all the handlers defined in its superclass. Since it does not specify any annotations
|
||||||
|
* it should be considered a message lister
|
||||||
|
*
|
||||||
|
* @author bennidi
|
||||||
|
* Date: 11/22/12
|
||||||
|
*/
|
||||||
|
public class NonListeningBean extends EventingTestBean{
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleTestEvent(TestEvent event) {
|
||||||
|
event.counter.incrementAndGet(); // should never be called
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSubTestEvent(SubTestEvent event) {
|
||||||
|
event.counter.incrementAndGet(); // should never be called
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleFiltered(SubTestEvent event) {
|
||||||
|
event.counter.incrementAndGet(); // should never be called
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user