pax_global_header00006660000000000000000000000064131463652170014522gustar00rootroot0000000000000052 comment=3e7232147c5a7bfcadd36f13c9c3f9822c3bb552 mbassador-1.3.1/000077500000000000000000000000001314636521700134775ustar00rootroot00000000000000mbassador-1.3.1/.gitignore000066400000000000000000000004321314636521700154660ustar00rootroot00000000000000# idea project settings # *.iml *.ipr *.iws .idea/* .idea # eclipse settings /.settings/ # Package Files # *.war *.ear # root of compiled classes # target/**/* target/** classes/ # the local maven repository # lib/ mvn-local-repo/**/* release.properties /.classpath /.project mbassador-1.3.1/.travis.yml000066400000000000000000000000521314636521700156050ustar00rootroot00000000000000jdk: - openjdk6 script: mvn clean testmbassador-1.3.1/LICENSE000066400000000000000000000020651314636521700145070ustar00rootroot00000000000000MIT License Copyright (c) 2012 Benjamin Diedrichsen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mbassador-1.3.1/README.md000066400000000000000000000274371314636521700147730ustar00rootroot00000000000000[![build status](https://travis-ci.org/bennidi/mbassador.svg?branch=master)](https://travis-ci.org/bennidi/mbassador) [![maven central](https://img.shields.io/maven-central/v/net.engio/mbassador.svg)](https://maven-badges.herokuapp.com/maven-central/net.engio/mbassador) [![javadoc](http://www.javadoc.io/badge/net.engio/mbassador.svg)](http://www.javadoc.io/doc/net.engio/mbassador) [![wiki](assets/wiki.png?raw=true)](wiki) MBassador ========= MBassador is a light-weight, high-performance event bus implementing the [publish subscribe pattern](https://en.wikipedia.org/wiki/Publish-subscribe_pattern). It is designed for ease of use and aims to be feature rich and extensible while preserving resource efficiency and performance. The core of MBassador is built around a *custom data structure* that provides **non-blocking reads** and minimized lock contention for writes such that performance degradation of concurrent read/write access is minimal. Benchmarks that illustrate the advantages of this design are available in this [github repository](https://github.com/bennidi/eventbus-performance). The code is **production ready**: 86% instruction coverage, 82% branch coverage with randomized and concurrently run test sets, no major bug has been reported in the last 18 month. No modifications to the core will be made without thoroughly testing the code. [Usage](#usage) | [Features](#features) | [Installation](#installation) | [Wiki](#wiki) | [Release Notes](#./changelog) | [Integrations](#integrations) | [Credits](#credits) | [Contribute](#contribute) | [License](#license)

Usage

Using MBassador in your project is very easy. Create as many instances of MBassador as you like (usually a singleton will do) ` bus = new MBassador()`, mark and configure your message handlers with `@Handler` annotations and finally register the listeners at any MBassador instance `bus.subscribe(aListener)`. Start sending messages to your listeners using one of MBassador's publication methods `bus.post(message).now()` or `bus.post(message).asynchronously()`. As a first reference, consider this illustrative example. You might want to have a look at the collection of [examples](./examples) to see its features on more detail. ```java // Define your handlers @Listener(references = References.Strong) class SimpleFileListener{ @Handler public void handle(File file){ // do something with the file } @Handler(delivery = Invoke.Asynchronously) public void expensiveOperation(File file){ // do something with the file } @Handler(condition = "msg.size >= 10000") @Enveloped(messages = {HashMap.class, LinkedList.class}) public void handleLarge(MessageEnvelope envelope) { // handle objects without common super type } } // somewhere else in your code MBassador bus = new MBassador(); bus.subscribe (new SimpleFileListener()); bus.post(new File("/tmp/smallfile.csv")).now(); bus.post(new File("/tmp/bigfile.csv")).asynchronously(); ``` ## Features > Annotation driven |Annotation|Function| |:-----|:-----| |`@Handler`|Mark a method as message handler| |`@Listener`|Can be used to customize listener wide configuration like the used reference type| |`@Enveloped`|A message envelope can be used to pass messages of different types into a single handler| |`@Filter`|Add filtering to prevent certain messages from being published| > Delivers everything, respects type hierarchy Messages do not need to implement any interface and can be of any type. The class hierarchy of a message is considered during message delivery, such that handlers will also receive subtypes of the message type they consume for - e.g. a handler of Object.class receives everything. Messages that do not match any handler result in the publication of a `DeadMessage` object which wraps the original message. DeadMessage events can be handled by registering listeners that handle DeadMessage. > Synchronous and asynchronous message delivery There are **two types of (a-)synchronicity** when using MBassador: message dispatch and handler invocation. **Message dispatch** _Synchronous_ dispatch means that the publish method blocks until all handlers have been *processed*. Note: This does not necessarily imply that each handler has been invoked and received the message - due to the possibility to combine synchronous dispatch with asynchronous handlers. This is the semantics of `publish(Object obj)` and `post(Objec obj).now()` _Asynchronous_ dispatch means that the publish method returns immediately and the message will be dispatched in another thread (fire and forget). This is the semantics of `publishAsync(Object obj)` and `post(Objec obj).asynchronously()` **Handler invocation** _Synchronous_ handlers are invoked sequentially and from the same thread within a running publication. _Asynchronous_ handlers means that the actual handler invocation is pushed to a queue that is processed by a pool of worker threads. > Configurable reference types By default, MBassador uses **weak references** for listeners to relieve the programmer of the need to explicitly unsubscribe listeners that are not used anymore and **avoid memory-leaks**. This is very comfortable in container managed environments where listeners are created and destroyed by frameworks, i.e. Spring, Guice etc. Just add everything to the bus, it will ignore objects without handlers and automatically clean-up orphaned weak references after the garbage collector has done its job. Instead of using weak references, a listener can be configured to be referenced using strong references using `@Listener(references=References.Strong)`. Strongly referenced listeners will stick around until explicitly unsubscribed. > Message filtering MBassador offers static message filtering. Filters are configured using annotations and multiple filters can be attached to a single message handler. Since version 1.2.0 Java EL expressions in `@Handler` are another way to define conditional message dispatch. Messages that have matching handlers but do not pass the configured filters result in the publication of a FilteredMessage object which wraps the original message. FilteredMessage events can be handled by registering listeners that handle FilteredMessage. Note: Since version 1.3.1 it is possible to wrap a filter in a custom annotation for reuse ```java public static final class RejectAllFilter implements IMessageFilter { @Override public boolean accepts(Object event, SubscriptionContext context) { return false; } } @IncludeFilters({@Filter(RejectAllFilter.class)}) @Retention(RetentionPolicy.RUNTIME) public @interface RejectAll {} public static class FilteredMessageListener{ // will cause republication of a FilteredEvent @Handler @RejectAll public void handleNone(Object any){ FilteredEventCounter.incrementAndGet(); } } ``` > Enveloped messages Message handlers can declare to receive an enveloped message using `Enveloped`. The envelope can wrap different types of messages to allow a single handler to handle multiple, unrelated message types. > Handler priorities A handler can be associated with a priority to influence the order in which messages are delivered when multiple matching handlers exist > Custom error handling Errors during message delivery are sent to all registered error handlers which can be added to the bus as necessary. > Extensibility MBassador is designed to be extensible with custom implementations of various components like message dispatchers and handler invocations (using the decorator pattern), metadata reader (you can add your own annotations) and factories for different kinds of objects. A configuration object is used to customize the different configurable parts, see [Features](https://github.com/bennidi/mbassador/wiki/Components#Feature) ## Installation MBassador is available from the Maven Central Repository using the following coordinates: ```xml net.engio mbassador {see.git.tags.for.latest.version} ``` You can also download binary release and javadoc from the [maven central repository](http://search.maven.org/#search|ga|1|mbassador). Of course you can always clone the repository and build from source. ## Documentation There is ongoing effort to extend documentation and provide code samples and detailed explanations of how the message bus works. Code samples can also be found in the various test cases. Please read about the terminology used in this project to avoid confusion and misunderstanding. + [javadoc](http://bennidi.github.io/mbassador/) + [wiki](wiki) + API examples on programcreek: [Handler](http://www.programcreek.com/java-api-examples/index.php?api=net.engio.mbassy.listener.Handler), [BusConfiguration](http://www.programcreek.com/java-api-examples/index.php?api=net.engio.mbassy.bus.config.BusConfiguration), [MBassador](http://www.programcreek.com/java-api-examples/index.php?api=net.engio.mbassy.bus.MBassador) ## Integrations There is a [spring-extension](https://github.com/bennidi/mbassador-spring) available to support CDI-like transactional message sending in a Spring environment. This is a good example of integration with other frameworks. Another example is the [Guice integration](https://github.com/bennidi/mbassador/wiki/Guice-Integration). ## Credits The initial inspiration for creating this component comes from Google Guava's event bus implementation. I liked the simplicity of its design and I trust in the code quality of google libraries. Unfortunately it uses strong references only. Thanks to all [contributors](https://github.com/bennidi/mbassador/pulls?q=is%3Apr+is%3Aclosed), especially + [arne-vandamme](http://github.com/arne-vandamme) for adding support for [meta-annotations](https://github.com/bennidi/mbassador/pull/74) + [Bernd Rosstauscher](http://github.com/Rossi1337) for providing an initial integration with JUEL + [David Sowerby](http://github.com/davidsowerby) for answering user questions, his tutorial on [guice integration](bennidi/mbassador/wiki/guice-integration) and his various PRs + [dorkbox](http://github.com/dorkbox) for various PRs and his [work on performance tuning](http://github.com/bennidi/eventbus-performance/issues/1) which is still to be integrated + [durron597](http://github.com/durron597) for his many PRs and the help he offered to other users Many thanks also to ej-technologies for providing an open source license of [![JProfiler](http://www.ej-technologies.com/images/banners/jprofiler_small.png)](http://www.ej-technologies.com/products/jprofiler/overview.html) and Jetbrains for a license of [IntelliJ IDEA](http://www.jetbrains.com/idea/) OSS used by MBassador: [jUnit](http://www.junit.org) | [maven](http://www.maven.org) | [mockito](http://www.mockito.org) | [slf4j](http://www.slf4j.org) | [Odysseus JUEL](http://juel.sourceforge.net/guide/start.html) ## Contribute Pick an issue from the list of open issues and start implementing. Make your PRs small and provide test code! Take a look at [this issue](bennidi/mbassador#109) for a good example. > Note: Due to the complexity of the data structure and synchronization code it took quite a while to get a stable core. New features will only be implemented if they do not require significant modification to the core. The primary focus of MBassador is to provide high-performance extended pub/sub. Sample code and documentation are both very appreciated contributions. Especially integration with different frameworks is of great value. Feel free and welcome to create Wiki pages to share your code and ideas. Example: [Guice integration](https://github.com/bennidi/mbassador/wiki/Guice-Integration) ## License This project is distributed under the terms of the MIT License. See file "LICENSE" for further reference. mbassador-1.3.1/assets/000077500000000000000000000000001314636521700150015ustar00rootroot00000000000000mbassador-1.3.1/assets/wiki.png000066400000000000000000000021421314636521700164510ustar00rootroot00000000000000PNG  IHDRL%g pHYs+tIME  8tEXtCommentCreated with GIMPWIDAThK2]ƏO~iJvSFuAhѶEZDPE-m -j9ShIt*y0Bw8=x~̨Au) @* Y]\.H$} F#T_$)٬}yy)%I[bnmmaԼHX,1H&}A.//1 ÔO5VaMR,^l6gWk(ѨxUH$8CEA{Z:OOO&0 "ZFA~p82j 1!*ZjeP(X,J>>>Eg2ѓ" `sssllL@JC NLL /%l5N嬓<ϳ===!!ihdd 4mX4 BH\ ÈwVT>_XXk4yXd2LR'Th44v 0. A4Mdooox<vww l6q'|~~~{{0Ƃ 0CCC*b뙙Vu Mzl6:::HF5r E"9u"IIN8UKWZ, Vk̪T*˲~j:6viz<755uww`0}P(bYj[ZZXZZF;;;$0Qe\ El6{ss399p1NnK?4Wyq+(bTh˫1A===ܜNkWHQkkkGGG2 @Enveloped.types + MessageEnvelope -> Envelope ### 1.3.2 + Merged PR #150: Filter definition as reusable annotation ### 1.3.0 + Non-Breaking API changes + Extended IMessagePublication to allow for error reporting using `hasError()` and `getError()` + Any publication method now returns an IMessagePublication object. This resolves [PR-127](../pull/127). Any dispatched message publication can now be inspected for execution error. Does not support collection of multiple errors due to implied GC and memory allocation overhead in high-throughput scenarios. + Breaking API changes + Added MessagePublication to IHandlerInvocation.invoke(...) + Added MessagePublication to IMessageDispatcher.dispatch(...) ### 1.2.4.2 + Updated pom. Now using nexus-staging plugin + Removed pmd + Fixed #135 and #136 ### [1.2.4](http://github.com/bennidi/mbassador/milestones/1.2.4) + API-Changes: + Remove IBusConfiguration.{handleError,addConfigurationErrorHandler} => Configuration errors are communicated as RuntimeExceptions + Removed BusFactory => Use explicit feature based construction with BusConfiguration + Integrated JaCoCo test coverage report => run `mvn clean test -Djacoco` ### [1.2.3](http://github.com/bennidi/mbassador/milestones/1.2.2) + Upgraded to Maven 3 + Upgraded all plugins to most recent version + Fixed all issues that produced warnings on build output + reduced visibility of `AbstractPubSubSupport.handlePublication` error from `public` to `protected` + Integrated [performance improvements](https://github.com/bennidi/mbassador/pull/125) made by dorkbox + __API-Changes:__ + Moved method addPublicationErrorHandler from `IMessageBus` to `IBusConfiguration` + Default constructor of `MBassador` has no `IPublicationErrorHandler` registered and will fall back to console logging. See [#106](http://github.com/bennidi/mbassador/issues/106), [#107](http://github.com/bennidi/mbassador/issues/107) ### [1.2.2](http://github.com/bennidi/mbassador/milestones/1.2.2) + Due to technical problems during release creation this version had to be skipped (git tag not removable) + The respective release is 1.2.3 ### [1.2.1](http://github.com/bennidi/mbassador/milestones/1.2.1) + Centralized handling of common (and arbitrary) properties (see `BusConfiguration#setProperty` and `net.engio.mbassy.bus.config.IBusConfiguration.Properties`) + Each bus now has a configurable id and respective #toString() implementation (useful for debugging) + Each bus now has a default logger (System.out) for publication errors (exception in handlers) which can be replaced with BusConfiguration#setProperty + __API-Changes:__ + Interface `IMessageFilter` now receives the SubscriptionContext as second parameter. This gives access to the bus runtime within filter logic (useful for error propagation). -> Change your filters signature. You can access the `MessageHandler` object directly from the context. + Removed deprecated method `BusConfiguration.SyncAsync()` -> Use default constructor or feature based configuration instead + Deleted interface `ISyncMessageBus` since it was merely an aggregation of existing interfaces -> Replace with GenericMessagePublicationSupport ### 1.2.0 + Added support for conditional handlers using Java EL. Thanks to Bernd Rosstauscher for the initial implementation. + __BREAKING CHANGES__ in BusConfiguration + Complete redesign of configuration setup using Features instead of simple get/set parameters. This will allow to flexibly combine features and still be able to exclude those not available in certain environments,for example, threading and reflection in GWT (this will be part of future releases) + Properties formerly located in BusConfiguration now moved to their respective Feature class + Removed all SyncXX related interfaces and config implementations. There is now only one `BusConfiguration` with its corresponding interface which will be used for all types of message bus implementations ### 1.1.10 + Fixed broken sort order of prioritized handlers (see [#58](http://github.com/bennidi/mbassador/issues/58)) + Addressed issue #63 by making the constructor of `MessageHandler` use a map of properties and by replacing dependencies to all MBassador specific annotations with Java primitives and simple interfaces + Small refactorings (moved stuff around to have cleaner packaging) + MessageBus.getExecutor() is now deprecated and will be removed with next release -> use the runtime to get access to it. + Introduced BusFactory with convenience methods for creating bus instances for different message dispatching scenarios like asynchronous FIFO (asynchronous message publications guaranteed to be delivered in the order they occurred) + Renamed runtime property of `BusRuntime` "handler.async-service" to "handler.async.executor" ### 1.1.9 + Fixed memory leak reported in [#53](http://github.com/bennidi/mbassador/issues/53) ### 1.1.8 + Internal refactorings and code improvements + Fixed [#44](http://github.com/bennidi/mbassador/issues/44) [#45](http://github.com/bennidi/mbassador/issues/45) [#47](http://github.com/bennidi/mbassador/issues/47) + NOTE: This release has a known issue with weak references which introduces a memory leak and is fixed in 1.1.9. The version 1.1.8 is not available from the central repository ### 1.1.7 + Console Logger not added to message bus instances by default -> use addErrorHandler(IPublicationErrorHandler.ConsoleLogger) + Fixed race conditions in net.engio.mbassy.subscription.Subscription and of WeakConcurrentSet.contains() + Improved message hierarchy handling: Now interfaces, enums , (abstract) classes should work in all combinations + Prevented dispatcher threads from dying on exceptions + Improved test-infrastructure and increased test-coverage + Thanks for your feedback! ### 1.1.6 + Added support for choosing between strong and weak references using the new @Listener annotation. @Listener can be added to any class that defines message handlers and allows to configure which reference type is used + Custom handler invocations: It is possible to provide a custom handler invocation for each message handler, see "invocation" property of @Handler + Changed packaging to "bundle" to support OSGI environments + Synchronization of message handlers via @Synchronized: Handlers that are not thread-safe can be synchronized to guarantee that only one thread at a time can invoke that handler + Created a message bus implementation that does not use threading to support use in non-multi-threaded environments like GWT, see ISyncMessageBus ### 1.1.3 + Added support for FilteredMessage event + Renamed @Listener to @Handler and DeadEvent to DeadMessage to increase alignment with the established terminology. Sorry for the inconvenience since this will lead to compile errors but good old find&replace will do + Repackaging and refactoring of some parts + Introduced message publication factories as configurable components to make MBassador more extensible/customizable + Added more documentation and unit tests ### 1.1.1 + Added support for DeadMessage event + Introduced new property to @Handler annotation that allows to activate/deactivate any message handler + Full support of proxies created by cglib + Message handler inheritance changed! See wiki page about handler definition for more details. + Changed @Handler property "dispatch" to "delivery" and renamed the associated enumeration values to more precisely indicate their meaning + Added more unit tests ### 1.1.0 First stable release! + Refactoring and repackaging + More exhaustive unit tests + Installation from the central repository ### 1.0.6.RC + Fixed behaviour with capacity bound blocking queue such that there now are two methods to schedule a message asynchronously. One will block until capacity becomes available, the other will timeout after a specified amount of time. + Additional unit tests ### 1.0.5.RC + Added MessageEnvelope and @Enveloped annotation to configure handlers that might receive arbitrary message type + Added handler configuration property to @Handler annotation to move from message filtering to more specific implementation of this feature ### 1.0.4.RC + Introduced BusConfiguration as a central class to encapsulate configurational aspects mbassador-1.3.1/examples/000077500000000000000000000000001314636521700153155ustar00rootroot00000000000000mbassador-1.3.1/examples/BusConstruction.java000066400000000000000000000036651314636521700213360ustar00rootroot00000000000000import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.SyncMessageBus; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; /** * These examples show how to create instances of a message bus using its different constructors. * */ public class BusConstruction { public static void main(String[] args){ // Create a bus instance configured with reasonable defaults // NOTE: Since there is no publication error handler provided, the bus will fall back to // ConsoleLogger and print a hint about how to add publication error handlers MBassador unboundBus = new MBassador(); // Create a bus bound to handle messages of type String.class only // with a custom publication error handler MBassador stringOnlyBus = new MBassador(new IPublicationErrorHandler() { @Override public void handleError(PublicationError error) { // custom error handling logic here } }); // Use feature driven configuration to have more control over the configuration details MBassador featureDrivenBus = new MBassador(new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()) .addPublicationErrorHandler(new IPublicationErrorHandler.ConsoleLogger()) .setProperty(IBusConfiguration.Properties.BusId, "global bus")); // this is used for identification in #toString // The same construction patterns work for the synchronous message bus SyncMessageBus synchronousOnly = new SyncMessageBus(); } } mbassador-1.3.1/examples/ErrorHandling.java000066400000000000000000000036161314636521700207240ustar00rootroot00000000000000import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; /** * * @author bennidi * Date: 07.10.15 */ public class ErrorHandling { static final IPublicationErrorHandler illustrativeHandler = new IPublicationErrorHandler() { @Override public void handleError(PublicationError error) { error.getMessage(); // An error message to describe what went wrong error.getCause(); // The underlying exception error.getPublishedMessage(); // The message that was published (can be null) error.getListener(); // The listener that was invoked when the execption was thrown (can be null) error.getHandler(); // The message handler (Method) that was invoked when the execption was thrown (can be null) } }; public static void main(String[] args){ // An error handler can be passed as constructor argument MBassador bus = new MBassador(illustrativeHandler); // ...or as part of a configuration object MBassador featureDrivenBus = new MBassador(new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()) .addPublicationErrorHandler(new IPublicationErrorHandler.ConsoleLogger()) // <-- This is a default handlers .addPublicationErrorHandler(illustrativeHandler) // <-- It is possible to add multiple handlers .setProperty(IBusConfiguration.Properties.BusId, "global bus")); // this is used for identification in #toString } } mbassador-1.3.1/examples/ListenerDefinition.java000066400000000000000000000140311314636521700217550ustar00rootroot00000000000000import net.engio.mbassy.dispatch.HandlerInvocation; import net.engio.mbassy.listener.*; import net.engio.mbassy.subscription.MessageEnvelope; import net.engio.mbassy.subscription.SubscriptionContext; import java.io.File; import java.lang.annotation.*; /** * These examples show how to configure listeners and handlers based on the available configuration options. * * NOTE: The presented handler configurations compose very well because they are implemented using decorator pattern. * */ public class ListenerDefinition { /** * By default any listener will be stored using weak references {@link java.lang.ref.WeakReference}. * This implies that at any give point in time, listeners might be GC'ed if no other alive object holds a reference to it. * * In managed environments where object lifecycle is controlled * by a framework (Spring, Guice etc.) this is a very handy feature because no attentions needs * to be paid to proper unsubscription of listeners that have ended their life (session scoped beans etc.) * * NOTE: There is no dedicated maintenance task running to take care of GC'ed listeners. * Automatic cleanup of orphaned weak references is an embedded process done during message publication. * */ static class WeaklyReferencedListener{ // Handler definitions go here } /** * In case that there is no other mechanism managing references to the listeners and they should * just stick around until explicitly unsubscribed, listener classes need to be annotated accordingly. */ @Listener(references = References.Strong) static class StronglyReferencedListener{ // This listener will stay subscribed until explicitly unsubscribed } /** * This listeners demonstrates the very basic use cases of synchronous and asynchronous handler definitions. * */ static class SyncAsyncListener{ /** * Any published message will be delivered to this handler (as it consumes any object of type Object.class) * Delivery is done using synchronous invocation, i.e. the handler is called from the thread running the message * publication. * */ @Handler public void synchronousHandler(Object message) { // do something } /** * According to the handler configuration, this handler is invoked asynchronously, meaning that each handler * invocation runs in a thread different from the one that runs the initial message publication. * * This feature is useful for computationally expensive or IO-bound tasks. * */ @Handler(delivery = Invoke.Asynchronously) public void asynchronousHandler(File message) { // do something more expensive here } } static class FilteringListener{ /** * This handler consumes only strings (as there are no subtypes of final String.class). * Furthermore, each string is passed through the list of defined filters and the handler is * invoked only if all filters pass. In this case, only strings starting with 'http' will be handled. * */ @Handler(delivery = Invoke.Synchronously, filters = {@Filter(Urlfilter.class)}) public void httpUrlsOnly(String message) { } /** * Another way of controlling which messages are delivered to handlers is by using JUEL expressions. * These can be specified as conditions (no type checking etc.) and will be evaluated on the msg. * This particular condition will filter out all empty strings */ @Handler(condition = "!msg.isEmpty()") public void handleNonEmptyStrings(String msg) { } /** * */ @Handler(delivery = Invoke.Synchronously, rejectSubtypes = true) @Enveloped(messages = {Object.class, String.class}) public void handleUnrelatedMessageTypes(MessageEnvelope envelope) { // the envelope will contain either an instance of Object.class or String.class // if rejectSubtypes were set to 'false' (default) also subtypes of TestMessage or TestMessage2 would be allowed } static class Urlfilter implements IMessageFilter{ public boolean accepts(String message, SubscriptionContext context){ return message.startsWith("http"); } } } /** * Listeners can use custom code to invoke their handlers. Custom invocation logic is defined on a per-handler * basis (as the signature requires knowledge about concrete handler and message type). */ @Listener(references = References.Strong) static class CustomInvocationListener { @Handler(invocation = TimingInvocation.class) public void handle(File message) { // do timed operation here } public static class TimingInvocation extends HandlerInvocation { public TimingInvocation(SubscriptionContext context) { super(context); } @Override public void invoke(CustomInvocationListener listener, File message) { long start = System.currentTimeMillis(); listener.handle(message); long duration = System.currentTimeMillis() - start; System.out.println("Time takes for handler invocation: " + duration + " ms"); } } } /** * Handler annotation that adds a condition checking for positive integers only */ @Retention(value = RetentionPolicy.RUNTIME) @Inherited @Handler(condition = "msg.getClass() == Integer.class && msg > 0") @Synchronized @Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @interface SynchronizedPositiveIntegers{} static class ListenerWithCustomAnnotation{ @SynchronizedPositiveIntegers public void handlePositiveIntegers(Integer msg){ } } } mbassador-1.3.1/examples/SubscriptionAndPublication.java000066400000000000000000000034021314636521700234600ustar00rootroot00000000000000import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import java.io.File; /** * * @author bennidi * Date: 07.10.15 */ public class SubscriptionAndPublication { static MBassador bus = new MBassador(new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()) .addPublicationErrorHandler(new IPublicationErrorHandler.ConsoleLogger()) .setProperty(IBusConfiguration.Properties.BusId, "global bus")); // this is used for identification in #toString public static void main(String[] args){ // Listeners are subscribed by passing them to the #subscribe() method bus.subscribe(new ListenerDefinition.SyncAsyncListener()); // #subscribe() is idem-potent => Multiple calls to subscribe do NOT add the listener more than once (set semantics) Object listener = new ListenerDefinition.SyncAsyncListener(); bus.subscribe(listener); bus.subscribe(listener); // Classes without handlers will be silently ignored bus.subscribe(new Object()); bus.subscribe(new String()); bus.publishAsync(new File("/tmp/random.csv")); //returns immediately, publication will continue asynchronously bus.post(new File("/tmp/random.csv")).asynchronously(); // same as above bus.publish("some message"); // will return after each handler has been invoked bus.post("some message").now(); // same as above } } mbassador-1.3.1/pom.xml000066400000000000000000000326311314636521700150210ustar00rootroot00000000000000 4.0.0 net.engio mbassador 1.3.1 bundle mbassador Mbassador is a fast and flexible event bus system following the publish subscribe pattern. It is designed for ease of use and aims to be feature rich and extensible while preserving resource efficiency and performance. It provides non-blocking iterators and minimal write contention with low memory footprint. Some features: declarative handler definition via annotations, sync and/or async event delivery, weak or strong references, configurable event filters, https://github.com/bennidi/mbassador MIT license http://www.opensource.org/licenses/mit-license.php git@github.com:bennidi/mbassador.git scm:git:git@github.com:bennidi/mbassador.git mbassador-1.2.2 scm:git:git@github.com:bennidi/mbassador.git bennidi Benjamin Diedrichsen +1 b.diedrichsen@googlemail.com 1.6 UTF-8 1.6 file://${project.basedir}/mvn-local-repo ossrh https://oss.sonatype.org/content/repositories/snapshots ossrh https://oss.sonatype.org/service/local/staging/deploy/maven2/ junit junit 4.12 test org.mockito mockito-core 1.10.19 test org.slf4j slf4j-api 1.7.5 test org.slf4j slf4j-log4j12 1.7.5 test javax.el el-api provided 2.2 de.odysseus.juel juel-impl 2.2.7 runtime true de.odysseus.juel juel-spi 2.2.7 runtime true org.sonatype.plugins nexus-staging-maven-plugin 1.6.7 true ossrh https://oss.sonatype.org/ true org.apache.maven.plugins maven-gpg-plugin 1.6 sign-artifacts verify sign org.apache.felix maven-bundle-plugin 2.3.7 true ${project.groupId}.${project.artifactId} {local-packages} org.apache.maven.plugins maven-compiler-plugin 3.3 ${project.build.java.version} ${project.build.java.version} org.apache.maven.plugins maven-release-plugin 2.5.3 forked-path org.apache.maven.plugins maven-surefire-plugin 2.18.1 ${surefireArgLine} false AllTests.java org.apache.maven.plugins maven-source-plugin 2.4 attach-sources jar org.apache.maven.plugins maven-javadoc-plugin 2.10.3 true public true
mbassador, ${project.version}
mbassador, ${project.version}
mbassador, ${project.version} -Xdoclint:none
attach-javadocs jar
org.apache.maven.plugins maven-scm-publish-plugin 1.1 ${project.build.directory}/scmpublish Publishing javadoc for ${project.artifactId}:${project.version} ${project.reporting.outputDirectory}/apidocs true scm:git:git@github.com:bennidi/mbassador.git gh-pages
jacoco-coverage jacoco org.jacoco jacoco-maven-plugin 0.7.5.201505241946 pre-unit-test prepare-agent ${project.build.directory}/coverage-reports/jacoco-ut.exec surefireArgLine post-unit-test test report ${project.build.directory}/coverage-reports/jacoco-ut.exec ${project.reporting.outputDirectory}/jacoco-ut
mbassador-1.3.1/src/000077500000000000000000000000001314636521700142665ustar00rootroot00000000000000mbassador-1.3.1/src/main/000077500000000000000000000000001314636521700152125ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/000077500000000000000000000000001314636521700161335ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/000077500000000000000000000000001314636521700167215ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/000077500000000000000000000000001314636521700200225ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/000077500000000000000000000000001314636521700213205ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/000077500000000000000000000000001314636521700221115ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/AbstractPubSubSupport.java000066400000000000000000000111451314636521700272370ustar00rootroot00000000000000package net.engio.mbassy.bus; import net.engio.mbassy.bus.common.DeadMessage; import net.engio.mbassy.bus.common.PubSubSupport; import net.engio.mbassy.bus.config.ConfigurationError; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.subscription.Subscription; import net.engio.mbassy.subscription.SubscriptionManager; import java.util.*; import static net.engio.mbassy.bus.config.IBusConfiguration.Properties.BusId; import static net.engio.mbassy.bus.config.IBusConfiguration.Properties.PublicationErrorHandlers; /** * The base class for all message bus implementations. * * @param */ public abstract class AbstractPubSubSupport implements PubSubSupport { // this handler will receive all errors that occur during message dispatch or message handling private final List errorHandlers = new ArrayList(); private final MessagePublication.Factory publicationFactory; private final SubscriptionManager subscriptionManager; private final BusRuntime runtime; public static final String ERROR_HANDLER_MSG = "INFO: No error handler has been configured to handle exceptions during publication.\n" + "Publication error handlers can be added by IBusConfiguration.addPublicationErrorHandler()\n" + "Falling back to console logger."; public AbstractPubSubSupport(IBusConfiguration configuration) { //transfer publication error handlers from the config object this.errorHandlers.addAll(configuration.getRegisteredPublicationErrorHandlers()); if (errorHandlers.isEmpty()) { errorHandlers.add(new IPublicationErrorHandler.ConsoleLogger()); System.out.println(ERROR_HANDLER_MSG); } this.runtime = new BusRuntime(this) .add(PublicationErrorHandlers, configuration.getRegisteredPublicationErrorHandlers()) .add(BusId, configuration.getProperty(BusId, UUID.randomUUID().toString())); // configure the pub sub feature Feature.SyncPubSub pubSubFeature = configuration.getFeature(Feature.SyncPubSub.class); if(pubSubFeature == null){ throw ConfigurationError.MissingFeature(Feature.SyncPubSub.class); } this.subscriptionManager = pubSubFeature.getSubscriptionManagerProvider() .createManager(pubSubFeature.getMetadataReader(), pubSubFeature.getSubscriptionFactory(), runtime); this.publicationFactory = pubSubFeature.getPublicationFactory(); } protected MessagePublication.Factory getPublicationFactory() { return publicationFactory; } public Collection getRegisteredErrorHandlers() { return Collections.unmodifiableCollection(errorHandlers); } public boolean unsubscribe(Object listener) { return subscriptionManager.unsubscribe(listener); } public void subscribe(Object listener) { subscriptionManager.subscribe(listener); } @Override public BusRuntime getRuntime() { return runtime; } protected MessagePublication createMessagePublication(T message) { Collection subscriptions = getSubscriptionsByMessageType(message.getClass()); if ((subscriptions == null || subscriptions.isEmpty()) && !message.getClass() .equals(DeadMessage.class)) { // DeadMessage Event subscriptions = getSubscriptionsByMessageType(DeadMessage.class); return getPublicationFactory().createPublication(runtime, subscriptions, new DeadMessage(message)); } else { return getPublicationFactory().createPublication(runtime, subscriptions, message); } } // obtain the set of subscriptions for the given message type // Note: never returns null! protected Collection getSubscriptionsByMessageType(Class messageType) { return subscriptionManager.getSubscriptionsByMessageType(messageType); } protected void handlePublicationError(PublicationError error) { for (IPublicationErrorHandler errorHandler : errorHandlers) { try { errorHandler.handleError(error); } catch (Throwable ex) { ex.printStackTrace(); } } } @Override public String toString() { return getClass().getSimpleName() + "(" + runtime.get(IBusConfiguration.Properties.BusId) + ")"; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/AbstractSyncAsyncMessageBus.java000066400000000000000000000120651314636521700303350ustar00rootroot00000000000000package net.engio.mbassy.bus; import net.engio.mbassy.bus.common.IMessageBus; import net.engio.mbassy.bus.config.ConfigurationError; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.InternalPublicationError; import net.engio.mbassy.bus.publication.ISyncAsyncPublicationCommand; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; /** * The base class for all message bus implementations with support for asynchronous message dispatch * * @param The type of message this bus consumes * @param

The publication commands this bus supports depend on P */ public abstract class AbstractSyncAsyncMessageBus extends AbstractPubSubSupport implements IMessageBus { // executor for asynchronous message handlers private final ExecutorService executor; // all threads that are available for asynchronous message dispatching private final List dispatchers; // all pending messages scheduled for asynchronous dispatch are queued here private final BlockingQueue pendingMessages; protected AbstractSyncAsyncMessageBus(IBusConfiguration configuration) { super(configuration); // configure asynchronous message dispatch Feature.AsynchronousMessageDispatch asyncDispatch = configuration.getFeature(Feature.AsynchronousMessageDispatch.class); if(asyncDispatch == null){ throw ConfigurationError.MissingFeature(Feature.AsynchronousMessageDispatch.class); } pendingMessages = asyncDispatch.getMessageQueue(); dispatchers = new ArrayList(asyncDispatch.getNumberOfMessageDispatchers()); initDispatcherThreads(asyncDispatch); // configure asynchronous handler invocation Feature.AsynchronousHandlerInvocation asyncInvocation = configuration.getFeature(Feature.AsynchronousHandlerInvocation.class); if(asyncInvocation == null){ throw ConfigurationError.MissingFeature(Feature.AsynchronousHandlerInvocation.class); } this.executor = asyncInvocation.getExecutor(); getRuntime().add(IBusConfiguration.Properties.AsynchronousHandlerExecutor, executor); } // initialize the dispatch workers private void initDispatcherThreads(Feature.AsynchronousMessageDispatch configuration) { for (int i = 0; i < configuration.getNumberOfMessageDispatchers(); i++) { // each thread will run forever and process incoming // message publication requests Thread dispatcher = configuration.getDispatcherThreadFactory().newThread(new Runnable() { public void run() { while (true) { IMessagePublication publication = null; try { publication = pendingMessages.take(); publication.execute(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } catch(Throwable t){ handlePublicationError(new InternalPublicationError(t, "Error in asynchronous dispatch",publication)); } } } }); dispatcher.setName("MsgDispatcher-"+i); dispatchers.add(dispatcher); dispatcher.start(); } } // this method queues a message delivery request protected IMessagePublication addAsynchronousPublication(MessagePublication publication) { try { pendingMessages.put(publication); return publication.markScheduled(); } catch (InterruptedException e) { handlePublicationError(new InternalPublicationError(e, "Error while adding an asynchronous message publication", publication)); return publication; } } // this method queues a message delivery request protected IMessagePublication addAsynchronousPublication(MessagePublication publication, long timeout, TimeUnit unit) { try { return pendingMessages.offer(publication, timeout, unit) ? publication.markScheduled() : publication; } catch (InterruptedException e) { handlePublicationError(new InternalPublicationError(e, "Error while adding an asynchronous message publication", publication)); return publication; } } @Override protected void finalize() throws Throwable { super.finalize(); shutdown(); } @Override public void shutdown() { for (Thread dispatcher : dispatchers) { dispatcher.interrupt(); } if(executor != null) executor.shutdown(); } @Override public boolean hasPendingMessages() { return pendingMessages.size() > 0; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/BusRuntime.java000066400000000000000000000031721314636521700250540ustar00rootroot00000000000000package net.engio.mbassy.bus; import net.engio.mbassy.bus.common.PubSubSupport; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.MissingPropertyException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * Message bus implementations potentially vary in the features they provide and consequently in the components and properties * they expose. The runtime is a container for all those dynamic properties and components and is meant to be passed around * between collaborating objects such that they may access the different functionality provided by the bus implementation * they all belong to. * * It is the responsibility of the bus implementation to create and configure the runtime according to its capabilities, * */ public class BusRuntime { private PubSubSupport provider; private Map properties = new HashMap(); public BusRuntime(PubSubSupport provider) { this.provider = provider; } public T get(String key){ if(!contains(key)) throw new MissingPropertyException("The property " + key + " is not available in this runtime"); else return (T) properties.get(key); } public PubSubSupport getProvider(){ return provider; } public Collection getKeys(){ return properties.keySet(); } public BusRuntime add(String key, Object property){ properties.put(key, property); return this; } public boolean contains(String key){ return properties.containsKey(key); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/IMessagePublication.java000066400000000000000000000016231314636521700266450ustar00rootroot00000000000000package net.engio.mbassy.bus; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.subscription.Subscription; /** * A message publication is created for each asynchronous message dispatch. It reflects the state * of the corresponding message publication process, i.e. provides information whether the * publication was successfully scheduled, is currently running etc. *

* A message publication lives within a single thread. It is not designed in a thread-safe manner -> not eligible to * be used in multiple threads simultaneously . * * @author bennidi * Date: 11/16/12 */ public interface IMessagePublication { void execute(); boolean isFinished(); boolean isRunning(); boolean isScheduled(); boolean hasError(); PublicationError getError(); boolean isDeadMessage(); boolean isFilteredMessage(); Object getMessage(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/MBassador.java000066400000000000000000000055711314636521700246370ustar00rootroot00000000000000package net.engio.mbassy.bus; import net.engio.mbassy.bus.common.IMessageBus; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.bus.publication.SyncAsyncPostCommand; import java.util.concurrent.TimeUnit; public class MBassador extends AbstractSyncAsyncMessageBus> implements IMessageBus> { /** * Default constructor using default setup. super() will also add a default publication error logger */ public MBassador(){ this(new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default())); } /** * Construct with default settings and specified publication error handler * * @param errorHandler */ public MBassador(IPublicationErrorHandler errorHandler) { super(new BusConfiguration().addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()) .addPublicationErrorHandler(errorHandler)); } /** * Construct with fully specified configuration * * @param configuration */ public MBassador(IBusConfiguration configuration) { super(configuration); } public IMessagePublication publishAsync(T message) { return addAsynchronousPublication(createMessagePublication(message)); } public IMessagePublication publishAsync(T message, long timeout, TimeUnit unit) { return addAsynchronousPublication(createMessagePublication(message), timeout, unit); } /** * Synchronously publish a message to all registered listeners (this includes listeners defined for super types) * The call blocks until every messageHandler has processed the message. * * @param message */ public IMessagePublication publish(T message) { IMessagePublication publication = createMessagePublication(message); try { publication.execute(); } catch (Throwable e) { handlePublicationError(new PublicationError() .setMessage("Error during publication of message") .setCause(e) .setPublication(publication)); } finally{ return publication; } } @Override public SyncAsyncPostCommand post(T message) { return new SyncAsyncPostCommand(this, message); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/MessagePublication.java000066400000000000000000000075511314636521700265420ustar00rootroot00000000000000package net.engio.mbassy.bus; import net.engio.mbassy.bus.common.DeadMessage; import net.engio.mbassy.bus.common.FilteredMessage; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.subscription.Subscription; import java.util.Collection; /** * A message publication is created for each asynchronous message dispatch. It reflects the state * of the corresponding message publication process, i.e. provides information whether the * publication was successfully scheduled, is currently running etc. *

* A message publication lives within a single thread. It is not designed in a thread-safe manner -> not eligible to * be used in multiple threads simultaneously . * * @author bennidi * Date: 11/16/12 */ public class MessagePublication implements IMessagePublication { private final Collection subscriptions; private final Object message; // message publications can be referenced by multiple threads to query publication progress private volatile State state = State.Initial; private volatile boolean dispatched = false; private final BusRuntime runtime; private PublicationError error = null; protected MessagePublication(BusRuntime runtime, Collection subscriptions, Object message, State initialState) { this.runtime = runtime; this.subscriptions = subscriptions; this.message = message; this.state = initialState; } public boolean add(Subscription subscription) { return subscriptions.add(subscription); } /* TODO: document state transitions */ public void execute() { state = State.Running; for (Subscription sub : subscriptions) { sub.publish(this, message); } state = State.Finished; // This part is necessary to support the feature of publishing a DeadMessage or FilteredMessage // in case that the original message has not made it to any listener. // This happens if subscriptions are empty (due to GC of weak listeners or explicit desubscription) // or if configured filters do not let a message pass. The flag is set by the dispatchers. // META: This seems to be a suboptimal design if (!dispatched) { if (!isFilteredMessage() && !isDeadMessage()) { runtime.getProvider().publish(new FilteredMessage(message)); } else if (!isDeadMessage()) { runtime.getProvider().publish(new DeadMessage(message)); } } } public boolean isFinished() { return state.equals(State.Finished); } public boolean isRunning() { return state.equals(State.Running); } public boolean isScheduled() { return state.equals(State.Scheduled); } public boolean hasError() { return this.error != null; } @Override public PublicationError getError() { return error; } public void markDispatched() { dispatched = true; } public void markError(PublicationError error) { this.error = error; } public MessagePublication markScheduled() { if (state.equals(State.Initial)) { state = State.Scheduled; } return this; } public boolean isDeadMessage() { return DeadMessage.class.equals(message.getClass()); } public boolean isFilteredMessage() { return FilteredMessage.class.equals(message.getClass()); } public Object getMessage() { return message; } private enum State { Initial, Scheduled, Running, Finished } public static class Factory { public MessagePublication createPublication(BusRuntime runtime, Collection subscriptions, Object message) { return new MessagePublication(runtime, subscriptions, message, State.Initial); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/SyncMessageBus.java000066400000000000000000000050521314636521700256510ustar00rootroot00000000000000package net.engio.mbassy.bus; import net.engio.mbassy.bus.common.ErrorHandlingSupport; import net.engio.mbassy.bus.common.GenericMessagePublicationSupport; import net.engio.mbassy.bus.common.PubSubSupport; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.bus.publication.IPublicationCommand; /** * A message bus implementation that offers only synchronous message publication. Using this bus * will not create any new threads. */ public class SyncMessageBus extends AbstractPubSubSupport implements PubSubSupport, ErrorHandlingSupport, GenericMessagePublicationSupport { /** * Default constructor using default setup. super() will also add a default publication error logger */ public SyncMessageBus() { super(new BusConfiguration().addFeature(Feature.SyncPubSub.Default())); } /** * Construct with default settings and specified publication error handler * @param errorHandler */ public SyncMessageBus(IPublicationErrorHandler errorHandler) { super(new BusConfiguration().addFeature(Feature.SyncPubSub.Default()).addPublicationErrorHandler(errorHandler)); } /** * Construct with fully specified configuration * * @param configuration */ public SyncMessageBus(IBusConfiguration configuration) { super(configuration); } @Override public IMessagePublication publish(T message) { IMessagePublication publication = createMessagePublication(message); try { publication.execute(); } catch (Throwable e) { handlePublicationError(new PublicationError().setMessage("Error during publication of message") .setCause(e) .setPublication(publication)); } finally{ return publication; } } @Override public SyncPostCommand post(T message) { return new SyncPostCommand(message); } public class SyncPostCommand implements IPublicationCommand { private T message; public SyncPostCommand(T message) { this.message = message; } @Override public IMessagePublication now() { return publish(message); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/000077500000000000000000000000001314636521700234015ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/DeadMessage.java000066400000000000000000000005311314636521700264050ustar00rootroot00000000000000package net.engio.mbassy.bus.common; /** * The dead message event is published whenever no message * handlers could be found for a given message publication. * * @author bennidi * Date: 1/18/13 */ public final class DeadMessage extends PublicationEvent { public DeadMessage(Object message) { super(message); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/ErrorHandlingSupport.java000066400000000000000000000015501314636521700304000ustar00rootroot00000000000000package net.engio.mbassy.bus.common; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import java.util.Collection; /** * Publication errors may occur at various points of time during message delivery. A handler may throw an exception, * may not be accessible due to security constraints or is not annotated properly. * In any of all possible cases a publication error is created and passed to each of the registered error handlers. * Error handlers can be added via the {@link IBusConfiguration}. * */ public interface ErrorHandlingSupport { /** * Get all registered instance of {@link IPublicationErrorHandler} * * @return an immutable collection containing all the registered error handlers */ Collection getRegisteredErrorHandlers(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/FilteredMessage.java000066400000000000000000000006721314636521700273140ustar00rootroot00000000000000package net.engio.mbassy.bus.common; /** * A filtered message event is published when there have been matching subscriptions for a given * message publication but configured filters prevented the message from being delivered to * any of the handlers. * * @author bennidi * Date: 3/1/13 */ public final class FilteredMessage extends PublicationEvent { public FilteredMessage(Object event) { super(event); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/GenericMessagePublicationSupport.java000066400000000000000000000022071314636521700327150ustar00rootroot00000000000000package net.engio.mbassy.bus.common; import net.engio.mbassy.bus.publication.IPublicationCommand; /** * This interface is meant to be implemented by different bus implementations to offer a consistent way * to plugin different methods of message publication. * * The parametrization of the IPostCommand influences which publication methods (asynchronous, synchronous or * conditional etc.) are available. * */ public interface GenericMessagePublicationSupport extends PubSubSupport, ErrorHandlingSupport{ /** * Publish a message to the bus using on of its supported message publication mechanisms. The supported * mechanisms depend on the available implementation and are exposed as subclasses of IPublicationCommand. * The standard mechanism is the synchronous dispatch which will publish the message in the current thread * and returns after every matching handler has been invoked. * * @param message - Any subtype of T welcome * @return An object that provides access to the available publication methods supported by the message bus. */ P post(T message); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/IMessageBus.java000066400000000000000000000100511314636521700264100ustar00rootroot00000000000000package net.engio.mbassy.bus.common; import net.engio.mbassy.bus.publication.ISyncAsyncPublicationCommand; /** * A message bus offers facilities for publishing messages to the message handlers of registered listeners. * A message publication starts when an object is send to the bus using one of the its publication methods. * * Messages can be published synchronously or asynchronously and may be of any type that is a valid sub type of the type parameter T. * Message handlers can be invoked synchronously or asynchronously depending on their configuration. Thus, there * are two notions of synchronicity / asynchronicity. One on the caller side, e.g. the invocation of the message publishing * methods. The second on the handler side, e.g. whether the handler is invoked in the same or a different thread. * *

* Each message publication is isolated from all other running publications such that it does not interfere with them. * Hence, the bus generally expects message handlers to be stateless as it may invoke them concurrently if multiple * messages get published asynchronously. If handlers are stateful and not thread-safe they can be marked to be invoked * in a synchronized fashion using @Synchronized annotation * *

* A listener is any object that defines at least one message handler and that has been subscribed to at least * one message bus. A message handler can be any method that accepts exactly one parameter (the message) and is marked * as a message handler using the @Handler annotation. * *

* By default, the bus uses weak references to all listeners such that registered listeners do not need to * be explicitly unregistered to be eligible for garbage collection. Dead (garbage collected) listeners are * removed on-the-fly as messages get dispatched. This can be changed using the @Listener annotation. * *

* Generally message handlers will be invoked in inverse sequence of subscription but any * client using this bus should not rely on this assumption. The basic contract of the bus is that it will deliver * a specific message exactly once to each of the respective message handlers. * *

* Messages are dispatched to all listeners that accept the type or supertype of the dispatched message. Additionally * a message handler may define filters to narrow the set of messages that it accepts. * *

* Subscribed message handlers are available to all pending message publications that have not yet started processing. * Any message listener may only be subscribed once -> subsequent subscriptions of an already subscribed message listener * will be silently ignored) * *

* Removing a listener (unsubscribing) means removing all subscribed message handlers of that listener. This remove operation * immediately takes effect and on all running dispatch processes -> A removed listener (a listener * is considered removed after the remove(Object) call returned) will under no circumstances receive any message publications. * Any running message publication that has not yet delivered the message to the removed listener will not see the listener * after the remove operation completed. * *

* NOTE: Generic type parameters of messages will not be taken into account, e.g. a List will * get dispatched to all message handlers that take an instance of List as their parameter * * @author bennidi * Date: 2/8/12 */ public interface IMessageBus extends GenericMessagePublicationSupport{ /** * {@inheritDoc} */ @Override P post(T message); /** * Check whether any asynchronous message publications are pending to be processed * * @return true if any unfinished message publications are found */ boolean hasPendingMessages(); /** * Shutdown the bus such that it will stop delivering asynchronous messages. Executor service and * other internally used threads will be shutdown gracefully. After calling shutdown it is not safe * to further use the message bus. */ void shutdown(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/ISyncMessageBus.java000066400000000000000000000004671314636521700272570ustar00rootroot00000000000000package net.engio.mbassy.bus.common; import net.engio.mbassy.bus.publication.IPublicationCommand; /** * @author bennidi * Date: 3/29/13 */ public interface ISyncMessageBus extends PubSubSupport, ErrorHandlingSupport, GenericMessagePublicationSupport{ } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/PubSubSupport.java000066400000000000000000000034211314636521700270410ustar00rootroot00000000000000package net.engio.mbassy.bus.common; import net.engio.mbassy.bus.IMessagePublication; /** * This interface defines the very basic message publication semantics according to the publish subscribe pattern. * Listeners can be subscribed and unsubscribed using the corresponding methods. When a listener is subscribed its * handlers will be registered and start to receive matching message publications. * */ public interface PubSubSupport extends RuntimeProvider{ /** * Subscribe all handlers of the given listener. Any listener is only subscribed once * -> subsequent subscriptions of an already subscribed listener will be silently ignored * * @param listener */ void subscribe(Object listener); /** * Immediately remove all registered message handlers (if any) of the given listener. When this call returns all handlers * have effectively been removed and will not receive any messages (provided that running publications (iterators) in other threads * have not yet obtained a reference to the listener) *

* A call to this method passing any object that is not subscribed will not have any effect and is silently ignored. * * @param listener * @return true, if the listener was found and successfully removed * false otherwise */ boolean unsubscribe(Object listener); /** * Synchronously publish a message to all registered listeners. This includes listeners defined for super types of the * given message type, provided they are not configured to reject valid subtype. The call returns when all matching handlers * of all registered listeners have been notified (invoked) of the message. * * @param message */ IMessagePublication publish(T message); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/PublicationEvent.java000066400000000000000000000007161314636521700275230ustar00rootroot00000000000000package net.engio.mbassy.bus.common; /** * A wrapped event is created when various conditions are matched (these depend on the concrete * (sub)type of wrapped event). * * @author bennidi * Date: 3/1/13 */ public abstract class PublicationEvent { private Object relatedMessage; public PublicationEvent(Object message) { this.relatedMessage = message; } public Object getMessage() { return relatedMessage; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/common/RuntimeProvider.java000066400000000000000000000004021314636521700273760ustar00rootroot00000000000000package net.engio.mbassy.bus.common; import net.engio.mbassy.bus.BusRuntime; /** * Each message bus provides a runtime object to access its dynamic features and runtime configuration. */ public interface RuntimeProvider { BusRuntime getRuntime(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/config/000077500000000000000000000000001314636521700233565ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/config/BusConfiguration.java000066400000000000000000000032461314636521700275070ustar00rootroot00000000000000package net.engio.mbassy.bus.config; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import java.util.*; /** * {@inheritDoc} */ public class BusConfiguration implements IBusConfiguration { // the registered properties private final Map properties = new HashMap(); // these are transferred to the bus to receive all errors that occur during message dispatch or message handling private final List publicationErrorHandlers = new ArrayList(); public BusConfiguration() { super(); } @Override public IBusConfiguration setProperty(String name, Object value) { properties.put(name, value); return this; } @Override public T getProperty(String name, T defaultValue) { return properties.containsKey(name) ? (T) properties.get(name) : defaultValue; } @Override public boolean hasProperty(String name) { return properties.containsKey(name); } @Override public T getFeature(Class feature) { return (T) properties.get(feature); } @Override public IBusConfiguration addFeature(Feature feature) { properties.put(feature.getClass(), feature); return this; } @Override public final BusConfiguration addPublicationErrorHandler(IPublicationErrorHandler handler) { publicationErrorHandlers.add(handler); return this; } @Override public Collection getRegisteredPublicationErrorHandlers() { return Collections.unmodifiableCollection(publicationErrorHandlers); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/config/ConfigurationError.java000066400000000000000000000015361314636521700300470ustar00rootroot00000000000000package net.engio.mbassy.bus.config; /** * Configuration errors represent specific invalid configurations of a feature in a {@link net.engio.mbassy.bus.config.IBusConfiguration} * An invalid feature configuration is assumed to render the bus dysfunctional and as such is thrown as an unchecked exception. * * @author bennidi * Date: 8/29/14 */ public class ConfigurationError extends RuntimeException{ private String message; private ConfigurationError(String message) { this.message = message; } public static ConfigurationError MissingFeature(Class featureType){ return new ConfigurationError("The expected feature " + featureType + " was missing. Use addFeature() in IBusConfiguration to add features."); } @Override public String toString() { return message; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/config/ConfigurationErrorHandler.java000066400000000000000000000007421314636521700313430ustar00rootroot00000000000000package net.engio.mbassy.bus.config; /** * Respond to a {@link net.engio.mbassy.bus.config.ConfigurationError} with any kind of action. * * @author bennidi * Date: 8/29/14 */ public interface ConfigurationErrorHandler { /** * Called when a misconfiguration is detected on a {@link net.engio.mbassy.bus.config.IBusConfiguration} * @param error The error that represents the detected misconfiguration. */ void handle(ConfigurationError error); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/config/Feature.java000066400000000000000000000142451314636521700256220ustar00rootroot00000000000000package net.engio.mbassy.bus.config; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.listener.MetadataReader; import net.engio.mbassy.subscription.ISubscriptionManagerProvider; import net.engio.mbassy.subscription.SubscriptionFactory; import net.engio.mbassy.subscription.SubscriptionManagerProvider; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; /** * A feature defines the configuration of a specific functionality of a message bus. * * @author bennidi * Date: 8/29/14 */ public interface Feature { class SyncPubSub implements Feature{ public static final SyncPubSub Default(){ return new SyncPubSub() .setMetadataReader(new MetadataReader()) .setPublicationFactory(new MessagePublication.Factory()) .setSubscriptionFactory(new SubscriptionFactory()) .setSubscriptionManagerProvider(new SubscriptionManagerProvider()); } private MessagePublication.Factory publicationFactory; private MetadataReader metadataReader; private SubscriptionFactory subscriptionFactory; private ISubscriptionManagerProvider subscriptionManagerProvider; public ISubscriptionManagerProvider getSubscriptionManagerProvider() { return subscriptionManagerProvider; } public SyncPubSub setSubscriptionManagerProvider(ISubscriptionManagerProvider subscriptionManagerProvider) { this.subscriptionManagerProvider = subscriptionManagerProvider; return this; } public SubscriptionFactory getSubscriptionFactory() { return subscriptionFactory; } public SyncPubSub setSubscriptionFactory(SubscriptionFactory subscriptionFactory) { this.subscriptionFactory = subscriptionFactory; return this; } public MetadataReader getMetadataReader() { return metadataReader; } public SyncPubSub setMetadataReader(MetadataReader metadataReader) { this.metadataReader = metadataReader; return this; } /** * The message publication factory is used to wrap a published message * in a {@link MessagePublication} for processing. * @return The factory to be used by the bus to create the publications */ public MessagePublication.Factory getPublicationFactory() { return publicationFactory; } public SyncPubSub setPublicationFactory(MessagePublication.Factory publicationFactory) { this.publicationFactory = publicationFactory; return this; } } class AsynchronousHandlerInvocation implements Feature{ protected static final ThreadFactory MessageHandlerThreadFactory = new ThreadFactory() { private final AtomicInteger threadID = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread thread = Executors.defaultThreadFactory().newThread(r); thread.setName("AsyncHandler-" + threadID.getAndIncrement()); thread.setDaemon(true); return thread; } }; public static final AsynchronousHandlerInvocation Default(){ int numberOfCores = Runtime.getRuntime().availableProcessors(); return Default(numberOfCores, numberOfCores * 2); } public static final AsynchronousHandlerInvocation Default(int minThreadCount, int maxThreadCount){ return new AsynchronousHandlerInvocation().setExecutor(new ThreadPoolExecutor(minThreadCount, maxThreadCount, 1, TimeUnit.MINUTES, new LinkedBlockingQueue(), MessageHandlerThreadFactory)); } private ExecutorService executor; public ExecutorService getExecutor() { return executor; } public AsynchronousHandlerInvocation setExecutor(ExecutorService executor) { this.executor = executor; return this; } } class AsynchronousMessageDispatch implements Feature{ protected static final ThreadFactory MessageDispatchThreadFactory = new ThreadFactory() { private final AtomicInteger threadID = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread thread = Executors.defaultThreadFactory().newThread(r); thread.setDaemon(true);// do not prevent the JVM from exiting thread.setName("Dispatcher-" + threadID.getAndIncrement()); return thread; } }; public static final AsynchronousMessageDispatch Default(){ return new AsynchronousMessageDispatch() .setNumberOfMessageDispatchers(2) .setDispatcherThreadFactory(MessageDispatchThreadFactory) .setMessageQueue(new LinkedBlockingQueue(Integer.MAX_VALUE)); } private int numberOfMessageDispatchers; private BlockingQueue messageQueue; private ThreadFactory dispatcherThreadFactory; public int getNumberOfMessageDispatchers() { return numberOfMessageDispatchers; } public AsynchronousMessageDispatch setNumberOfMessageDispatchers(int numberOfMessageDispatchers) { this.numberOfMessageDispatchers = numberOfMessageDispatchers; return this; } public BlockingQueue getMessageQueue() { return messageQueue; } public AsynchronousMessageDispatch setMessageQueue(BlockingQueue pendingMessages) { this.messageQueue = pendingMessages; return this; } public ThreadFactory getDispatcherThreadFactory() { return dispatcherThreadFactory; } public AsynchronousMessageDispatch setDispatcherThreadFactory(ThreadFactory dispatcherThreadFactory) { this.dispatcherThreadFactory = dispatcherThreadFactory; return this; } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/config/IBusConfiguration.java000066400000000000000000000061161314636521700276170ustar00rootroot00000000000000package net.engio.mbassy.bus.config; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import java.util.Collection; /** * The configuration of message bus instances is feature driven, e.g. configuration parameters * are grouped into {@link Feature}. * * Each bus will look for the features it requires and configure them according to the provided configuration. * If a required feature is not found the bus will publish a {@link ConfigurationError} * to the {@link ConfigurationErrorHandler} * * @author bennidi. */ public interface IBusConfiguration{ /** * Set a property which will be read by the message bus constructor. Existing value will be overwritten. * Null values are supported (checking for existence of property will return true even if set to null). * * @param name The name of the property. Note: Each implementation may support different properties. * @param value The value of the property. * @return A reference to this bus configuration. */ IBusConfiguration setProperty(String name, Object value); /** * Read a property from this configuration. * * @param name The name of the property to be read. * @param defaultValue The value to be returned if property was not found * @param The type of property * @return The value associated with the given property name or defaultValue if not present */ T getProperty(String name, T defaultValue); /** * Check whether a property has been set. * * @return true if property was set (even if set to null) * false otherwise */ boolean hasProperty(String name); /** * Get a registered feature by its type (class). * */ T getFeature(Class feature); /** * Add a feature to the given configuration, replacing any existing feature of the same type. * * @param feature The feature to add * @return A reference to this bus configuration. */ IBusConfiguration addFeature(Feature feature); /** * Add a handler that will be called whenever a publication error occurs. * See {@link net.engio.mbassy.bus.error.PublicationError} * * @param handler The handler to be added to the list of handlers * @return A reference to this bus configuration. */ BusConfiguration addPublicationErrorHandler(IPublicationErrorHandler handler); /** * Get an unmodifiable collection of all registered publication error handlers */ Collection getRegisteredPublicationErrorHandlers(); /** * A collection of properties commonly used by different parts of the library. * * @author bennidi * Date: 22.02.15 */ final class Properties { public static final String BusId = "bus.id"; public static final String PublicationErrorHandlers = "bus.handlers.error"; public static final String AsynchronousHandlerExecutor = "bus.handlers.async-executor"; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/error/000077500000000000000000000000001314636521700232425ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/error/IPublicationErrorHandler.java000066400000000000000000000027521314636521700310050ustar00rootroot00000000000000package net.engio.mbassy.bus.error; /** * Publication error handlers are provided with a publication error every time an * error occurs during message publication. * A handler might fail with an exception, not be accessible because of the presence * of a security manager or other reasons might lead to failures during the message publication process. *

* * @author bennidi * Date: 2/22/12 */ @SuppressWarnings("PMD.UnusedModifier") public interface IPublicationErrorHandler { /** * Handle the given publication error. * * @param error The PublicationError to handle. */ void handleError(PublicationError error); /** * The default error handler will simply log to standard out and * print the stack trace if available. */ final class ConsoleLogger implements IPublicationErrorHandler { private final boolean printStackTrace; public ConsoleLogger() { this(false); } public ConsoleLogger(boolean printStackTrace) { this.printStackTrace = printStackTrace; } /** * {@inheritDoc} */ @Override public void handleError(final PublicationError error) { // Printout the error itself System.out.println(error); // Printout the stacktrace from the cause. if (printStackTrace && error.getCause() != null) { error.getCause().printStackTrace(); } } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/error/InternalPublicationError.java000066400000000000000000000011601314636521700310630ustar00rootroot00000000000000package net.engio.mbassy.bus.error; import net.engio.mbassy.bus.IMessagePublication; /** * This type of publication error is used to communicate technical/library related errors as opposed to errors in client code, i.e. message handlers) * * @author bennidi * Date: 15.10.15 */ public class InternalPublicationError extends PublicationError{ public InternalPublicationError(Throwable cause, String message, IMessagePublication publication) { super(cause, message, publication); } public InternalPublicationError(Throwable cause, String message) { super(cause, message); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/error/MessageBusException.java000066400000000000000000000007341314636521700300260ustar00rootroot00000000000000package net.engio.mbassy.bus.error; /** * The universal exception type for message bus implementations. * * @author bennidi * Date: 3/29/13 */ public class MessageBusException extends Exception{ public MessageBusException(String message) { super(message); } public MessageBusException(String message, Throwable cause) { super(message, cause); } public MessageBusException(Throwable cause) { super(cause); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/error/MissingPropertyException.java000066400000000000000000000010431314636521700311400ustar00rootroot00000000000000package net.engio.mbassy.bus.error; /** * This exception is thrown when a property value that is unavailable at runtime is accessed. * It indicates that some parts of the runtime configuration are actually incompatible, * i.e. one component is trying to access a feature of another component that does not * provide the feature (e.g. asynchronous functionality within a synchronous bus) */ public class MissingPropertyException extends RuntimeException{ public MissingPropertyException(String message) { super(message); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/error/PublicationError.java000066400000000000000000000110071314636521700273670ustar00rootroot00000000000000package net.engio.mbassy.bus.error; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.subscription.SubscriptionContext; import java.lang.reflect.Method; /** * Publication errors are used to communicate exceptions that occur during message publication. * The most common reason is most likely an exception thrown during the execution of a message handler. * * The publication error contains details about to the cause and location where error occurred. * They are passed to all registered instances of {@link IPublicationErrorHandler} configured within * the {@link net.engio.mbassy.bus.config.IBusConfiguration} * * @author bennidi * Date: 2/22/12 * Time: 4:59 PM */ public class PublicationError{ // Internal state private Throwable cause; private String errorMsg; private Method handler; private Object listener; private IMessagePublication publication; private Object message; /** * Compound constructor, creating a PublicationError from the supplied objects. * * @param cause The Throwable giving rise to this PublicationError. * @param errorMsg The message to send. * @param handler The method where the error was created. * @param listener The object in which the PublicationError was generated. * @param publication The publication that errored */ public PublicationError(final Throwable cause, final String errorMsg, final Method handler, final Object listener, final IMessagePublication publication) { this.cause = cause; this.errorMsg = errorMsg; this.handler = handler; this.listener = listener; this.publication = publication; this.message = publication != null ? publication.getMessage() : null; } public PublicationError(final Throwable cause, final String errorMsg, final IMessagePublication publication) { this.cause = cause; this.errorMsg = errorMsg; } public PublicationError(final Throwable cause, final String errorMsg, final SubscriptionContext context) { this.cause = cause; this.errorMsg = errorMsg; this.handler = context.getHandler().getMethod(); } public PublicationError(Throwable cause, String errorMsg) { this.cause = cause; this.errorMsg = errorMsg; } /** * Default constructor. */ public PublicationError() { super(); } /** * @return The Throwable giving rise to this PublicationError. */ public Throwable getCause() { return cause; } /** * Assigns the cause of this PublicationError. * * @param cause A Throwable which gave rise to this PublicationError. * @return This PublicationError. */ public PublicationError setCause(Throwable cause) { this.cause = cause; return this; } public String getMessage() { return errorMsg; } public PublicationError setMessage(String message) { this.errorMsg = message; return this; } public PublicationError setPublishedMessage(Object message) { this.message = message; return this; } public Method getHandler() { return handler; } public PublicationError setHandler(Method handler) { this.handler = handler; return this; } public Object getListener() { return listener; } public PublicationError setListener(Object listener) { this.listener = listener; return this; } public Object getPublishedMessage() { return message; } public PublicationError setPublication(IMessagePublication publication) { this.publication = publication; return this; } /** * {@inheritDoc} */ @Override public String toString() { String newLine = System.getProperty("line.separator"); return "PublicationError{" + newLine + "\tcause=" + cause + newLine + "\tmessage='" + errorMsg + '\'' + newLine + "\thandler=" + handler + newLine + "\tlistener=" + listener + newLine + "\tpublishedMessage=" + getPublishedMessage() + '}'; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/publication/000077500000000000000000000000001314636521700244225ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/publication/IPublicationCommand.java000066400000000000000000000012071314636521700311460ustar00rootroot00000000000000package net.engio.mbassy.bus.publication; import net.engio.mbassy.bus.IMessagePublication; /** * A publication command is used as an intermediate object created by a call to the message bus' post method. * It encapsulates the message publication flavors provided by the message bus implementation that created the command. * Subclasses may extend this interface and add functionality, e.g. different dispatch schemes. */ public interface IPublicationCommand { /** * Execute the message publication immediately. This call blocks until every matching message handler * has been invoked. */ IMessagePublication now(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/publication/ISyncAsyncPublicationCommand.java000066400000000000000000000023201314636521700327760ustar00rootroot00000000000000package net.engio.mbassy.bus.publication; import net.engio.mbassy.bus.IMessagePublication; import java.util.concurrent.TimeUnit; /** * * */ public interface ISyncAsyncPublicationCommand extends IPublicationCommand { /** * Execute the message publication asynchronously. The behaviour of this method depends on the * configured queuing strategy: *

* If an unbound queuing strategy is used the call returns immediately. * If a bounded queue is used the call might block until the message can be placed in the queue. * * @return A message publication that can be used to access information about the state of */ IMessagePublication asynchronously(); /** * Execute the message publication asynchronously. The behaviour of this method depends on the * configured queuing strategy: *

* If an unbound queuing strategy is used the call returns immediately. * If a bounded queue is used the call will block until the message can be placed in the queue * or the timeout is reached. * * @return A message publication that wraps up the publication request */ IMessagePublication asynchronously(long timeout, TimeUnit unit); } mbassador-1.3.1/src/main/java/net/engio/mbassy/bus/publication/SyncAsyncPostCommand.java000066400000000000000000000017261314636521700313520ustar00rootroot00000000000000package net.engio.mbassy.bus.publication; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.IMessagePublication; import java.util.concurrent.TimeUnit; /** * This post command provides access to standard synchronous and asynchronous dispatch * * @author bennidi * Date: 11/12/12 */ public class SyncAsyncPostCommand implements ISyncAsyncPublicationCommand { private T message; private MBassador mBassador; public SyncAsyncPostCommand(MBassador mBassador, T message) { this.mBassador = mBassador; this.message = message; } @Override public IMessagePublication now() { return mBassador.publish(message); } @Override public IMessagePublication asynchronously() { return mBassador.publishAsync(message); } @Override public IMessagePublication asynchronously(long timeout, TimeUnit unit) { return mBassador.publishAsync(message, timeout, unit); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/common/000077500000000000000000000000001314636521700226105ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/common/AbstractConcurrentSet.java000066400000000000000000000146611314636521700277450ustar00rootroot00000000000000package net.engio.mbassy.common; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This data structure is optimized for non-blocking reads even when write operations occur. * Running read iterators will not be affected by add operations since writes always insert at the head of the * structure. Remove operations can affect any running iterator such that a removed element that has not yet * been reached by the iterator will not appear in that iterator anymore. * * @author bennidi * Date: 2/12/12 * @author dorkbox * Date: 22/2/15 */ public abstract class AbstractConcurrentSet implements Set { private static final AtomicLong id = new AtomicLong(); private final long ID = id.getAndIncrement(); // Internal state protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final Map> entries; // maintain a map of entries for O(1) lookup protected Entry head; // reference to the first element protected AbstractConcurrentSet(Map> entries) { this.entries = entries; } protected abstract Entry createEntry(T value, Entry next); @Override public boolean add(T element) { if (element == null) return false; Lock writeLock = lock.writeLock(); boolean changed; try { writeLock.lock(); changed = insert(element); } finally { writeLock.unlock(); } return changed; } @Override public boolean contains(Object element) { Lock readLock = lock.readLock(); ISetEntry entry; try { readLock.lock(); entry = entries.get(element); } finally { readLock.unlock(); } return entry != null && entry.getValue() != null; } /** * Inserts a new element at the head of the set. * Note: This method is expected to be synchronized by the calling code */ private boolean insert(T element) { if (!entries.containsKey(element)) { head = createEntry(element, head); entries.put(element, head); return true; } return false; } @Override public int size() { return entries.size(); } @Override public boolean isEmpty() { return head == null; } @Override public boolean addAll(Collection elements) { boolean changed = false; Lock writeLock = lock.writeLock(); try { writeLock.lock(); for (T element : elements) { if (element != null) { changed |= insert(element); } } } finally { writeLock.unlock(); } return changed; } @Override public boolean remove(Object element) { if (!contains(element)) { return false; } else { Lock writeLock = lock.writeLock(); try { writeLock.lock(); ISetEntry listelement = entries.get(element); if (listelement == null) { return false; //removed by other thread in the meantime } if (listelement != head) { listelement.remove(); } else { head = head.next(); //oldHead.clear(); // optimize for GC not possible because of potentially running iterators } entries.remove(element); } finally { writeLock.unlock(); } return true; } } @Override public Object[] toArray() { return this.entries.entrySet().toArray(); } @SuppressWarnings("hiding") @Override public T[] toArray(T[] a) { return this.entries.entrySet().toArray(a); } @Override public boolean containsAll(Collection c) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException("Not implemented"); } @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException("Not implemented"); } @Override public void clear() { Lock writeLock = this.lock.writeLock(); try { writeLock.lock(); head = null; entries.clear(); } finally { writeLock.unlock(); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (this.ID ^ this.ID >>> 32); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } @SuppressWarnings("rawtypes") AbstractConcurrentSet other = (AbstractConcurrentSet) obj; if (this.ID != other.ID) { return false; } return true; } public abstract static class Entry implements ISetEntry { private Entry next; private Entry predecessor; protected Entry(Entry next) { this.next = next; next.predecessor = this; } protected Entry() { } // Not thread-safe! must be synchronized in enclosing context @Override public void remove() { if (predecessor != null) { predecessor.next = next; if (next != null) { next.predecessor = predecessor; } } else if (next != null) { next.predecessor = null; } // Can not nullify references to help GC because running iterators might not see the entire set // if this element is their current element //next = null; //predecessor = null; } @Override public Entry next() { return next; } @Override public void clear() { next = null; } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/common/IPredicate.java000066400000000000000000000004341314636521700254650ustar00rootroot00000000000000package net.engio.mbassy.common; /** * Created with IntelliJ IDEA. * * @author bennidi * Date: 10/22/12 * Time: 9:33 AM * To change this template use File | Settings | File Templates. */ public interface IPredicate { boolean apply(T target); } mbassador-1.3.1/src/main/java/net/engio/mbassy/common/ISetEntry.java000066400000000000000000000004461314636521700253450ustar00rootroot00000000000000package net.engio.mbassy.common; /** * Todo: Add javadoc * * @author bennidi * Date: 3/29/13 */ public interface ISetEntry { T getValue(); // not thread-safe! must be synchronized in enclosing context void remove(); ISetEntry next(); void clear(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/common/ReflectionUtils.java000066400000000000000000000131431314636521700265700ustar00rootroot00000000000000package net.engio.mbassy.common; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * @author bennidi * Date: 2/16/12 * Time: 12:14 PM */ public class ReflectionUtils { public static Method[] getMethods(IPredicate condition, Class target) { ArrayList methods = new ArrayList(); getMethods(condition, target, methods); final Method[] array = new Method[methods.size()]; methods.toArray(array); return array; } public static void getMethods(IPredicate condition, Class target, ArrayList methods) { try { for ( Method method : target.getDeclaredMethods() ) { if ( condition.apply( method ) ) { methods.add( method ); } } } catch ( Exception e ) { //nop } if ( !target.equals( Object.class ) ) { getMethods(condition, target.getSuperclass(), methods); } } /** * Traverses the class hierarchy upwards, starting at the given subclass, looking * for an override of the given methods -> finds the bottom most override of the given * method if any exists * * @param overridingMethod * @param subclass */ public static Method getOverridingMethod( final Method overridingMethod, final Class subclass ) { Class current = subclass; while ( !current.equals( overridingMethod.getDeclaringClass() ) ) { try { return current.getDeclaredMethod( overridingMethod.getName(), overridingMethod.getParameterTypes() ); } catch ( NoSuchMethodException e ) { current = current.getSuperclass(); } } return null; } /** * Collect all directly and indirectly related super types (classes and interfaces) of * a given class. * * @param from The root class to start with * @return A set of classes, each representing a super type of the root class */ public static Class[] getSuperTypes(Class from) { ArrayList superclasses = new ArrayList(); collectInterfaces( from, superclasses ); while ( !from.equals( Object.class ) && !from.isInterface() ) { superclasses.add( from.getSuperclass() ); from = from.getSuperclass(); collectInterfaces( from, superclasses ); } final Class[] classes = new Class[superclasses.size()]; superclasses.toArray(classes); return classes; } public static void collectInterfaces( Class from, Collection accumulator ) { for ( Class intface : from.getInterfaces() ) { accumulator.add( intface ); collectInterfaces( intface, accumulator ); } } public static boolean containsOverridingMethod( final Method[] allMethods, final Method methodToCheck ) { final int length = allMethods.length; Method method; for (int i = 0; i < length; i++) { method = allMethods[i]; if ( isOverriddenBy( methodToCheck, method ) ) { return true; } } return false; } /** * Searches for an Annotation of the given type on the class. Supports meta annotations. * * @param from AnnotatedElement (class, method...) * @param annotationType Annotation class to look for. * @param Class of annotation type * @return Annotation instance or null */ private static A getAnnotation( AnnotatedElement from, Class annotationType, Set visited) { if( visited.contains(from) ) return null; visited.add(from); A ann = from.getAnnotation( annotationType ); if( ann != null) return ann; for ( Annotation metaAnn : from.getAnnotations() ) { ann = getAnnotation(metaAnn.annotationType(), annotationType, visited); if ( ann != null ) { return ann; } } return null; } public static A getAnnotation( AnnotatedElement from, Class annotationType){ return getAnnotation(from, annotationType, new HashSet()); } private static boolean isOverriddenBy( Method superclassMethod, Method subclassMethod ) { // if the declaring classes are the same or the subclass method is not defined in the subclass // 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 = subclassMethod.getParameterTypes(); // method must specify the same number of parameters //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; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/common/StrongConcurrentSet.java000066400000000000000000000034021314636521700274450ustar00rootroot00000000000000package net.engio.mbassy.common; import java.util.HashMap; import java.util.Iterator; /** * This implementation uses strong references to the elements. *

* * @author bennidi * Date: 2/12/12 */ public class StrongConcurrentSet extends AbstractConcurrentSet{ public StrongConcurrentSet() { super(new HashMap>()); } public Iterator iterator() { return new Iterator() { private ISetEntry current = head; public boolean hasNext() { return current != null; } public T next() { if (current == null) { return null; } else { T value = current.getValue(); current = current.next(); return value; } } public void remove() { if (current == null) { return; } ISetEntry newCurrent = current.next(); StrongConcurrentSet.this.remove(current.getValue()); current = newCurrent; } }; } @Override protected Entry createEntry(T value, Entry next) { return next != null ? new StrongEntry(value, next) : new StrongEntry(value); } public static class StrongEntry extends Entry { private T value; private StrongEntry(T value, Entry next) { super(next); this.value = value; } private StrongEntry(T value) { super(); this.value = value; } @Override public T getValue() { return value; } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/common/WeakConcurrentSet.java000066400000000000000000000073601314636521700270670ustar00rootroot00000000000000package net.engio.mbassy.common; import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.WeakHashMap; import java.util.concurrent.locks.Lock; /** * This implementation uses weak references to the elements. Iterators automatically perform cleanups of * garbage collected objects during iteration -> no dedicated maintenance operations need to be called or run in background. *

*

*

* * @author bennidi * Date: 2/12/12 */ public class WeakConcurrentSet extends AbstractConcurrentSet{ public WeakConcurrentSet() { super(new WeakHashMap>()); } public Iterator iterator() { return new Iterator() { // the current listelement of this iterator // used to keep track of the iteration process private ISetEntry current = head; // this method will remove all orphaned entries // until it finds the first entry whose value has not yet been garbage collected // the method assumes that the current element is already orphaned and will remove it private void removeOrphans(){ Lock writelock = lock.writeLock(); try{ writelock.lock(); do { ISetEntry orphaned = current; current = current.next(); if (orphaned == head) { head = head.next(); } orphaned.remove(); } while(current != null && current.getValue() == null); } finally { writelock.unlock(); } } public boolean hasNext() { if (current == null) return false; if (current.getValue() == null) { // trigger removal of orphan references // because a null value indicates that the value has been garbage collected removeOrphans(); return current != null; // if any entry is left then it will have a value } else { return true; } } public T next() { if (current == null) { return null; } T value = current.getValue(); if (value == null) { // auto-removal of orphan references removeOrphans(); return next(); } else { current = current.next(); return value; } } public void remove() { //throw new UnsupportedOperationException("Explicit removal of set elements is only allowed via the controlling set. Sorry!"); if (current == null) { return; } ISetEntry newCurrent = current.next(); WeakConcurrentSet.this.remove(current.getValue()); current = newCurrent; } }; } @Override protected Entry createEntry(T value, Entry next) { return next != null ? new WeakEntry(value, next) : new WeakEntry(value); } public static class WeakEntry extends Entry { private WeakReference value; private WeakEntry(T value, Entry next) { super(next); this.value = new WeakReference(value); } private WeakEntry(T value) { super(); this.value = new WeakReference(value); } @Override public T getValue() { return value.get(); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/000077500000000000000000000000001314636521700231175ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/AsynchronousHandlerInvocation.java000066400000000000000000000023421314636521700320060ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.subscription.AbstractSubscriptionContextAware; import java.util.concurrent.ExecutorService; /** * This invocation will schedule the wrapped (decorated) invocation to be executed asynchronously * * @author bennidi * Date: 11/23/12 */ public class AsynchronousHandlerInvocation extends AbstractSubscriptionContextAware implements IHandlerInvocation { private final IHandlerInvocation delegate; private final ExecutorService executor; public AsynchronousHandlerInvocation(IHandlerInvocation delegate) { super(delegate.getContext()); this.delegate = delegate; this.executor = delegate.getContext().getRuntime().get(IBusConfiguration.Properties.AsynchronousHandlerExecutor); } /** * {@inheritDoc} */ @Override public void invoke(final Object listener, final Object message, final MessagePublication publication){ executor.execute(new Runnable() { @Override public void run() { delegate.invoke(listener, message, publication); } }); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/DelegatingMessageDispatcher.java000066400000000000000000000014561314636521700313470ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.subscription.AbstractSubscriptionContextAware; /** * A delegating dispatcher wraps additional logic around a given delegate. Essentially its * an implementation of the decorator pattern. * * @author bennidi * Date: 3/1/13 */ public abstract class DelegatingMessageDispatcher extends AbstractSubscriptionContextAware implements IMessageDispatcher { private final IMessageDispatcher delegate; public DelegatingMessageDispatcher(IMessageDispatcher delegate) { super(delegate.getContext()); this.delegate = delegate; } protected IMessageDispatcher getDelegate() { return delegate; } @Override public IHandlerInvocation getInvocation() { return delegate.getInvocation(); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/EnvelopedMessageDispatcher.java000066400000000000000000000015111314636521700312150ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.subscription.MessageEnvelope; /** * The enveloped dispatcher will wrap published messages in an envelope before * passing them to their configured dispatcher. *

* All enveloped message handlers will have this dispatcher in their chain * * @author bennidi * Date: 12/12/12 */ public class EnvelopedMessageDispatcher extends DelegatingMessageDispatcher { public EnvelopedMessageDispatcher(IMessageDispatcher dispatcher) { super(dispatcher); } @Override public void dispatch(MessagePublication publication, Object message, Iterable listeners){ getDelegate().dispatch(publication, new MessageEnvelope(message), listeners); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/FilteredMessageDispatcher.java000066400000000000000000000025731314636521700310430ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.listener.IMessageFilter; /** * A dispatcher that implements message filtering based on the filter configuration * of the associated message handler. It will delegate message delivery to another * message dispatcher after having performed the filtering logic. * * @author bennidi * Date: 11/23/12 */ public final class FilteredMessageDispatcher extends DelegatingMessageDispatcher { private final IMessageFilter[] filter; public FilteredMessageDispatcher(IMessageDispatcher dispatcher) { super(dispatcher); this.filter = dispatcher.getContext().getHandler().getFilter(); } private boolean passesFilter(Object message) { if (filter == null) { return true; } else { for (IMessageFilter aFilter : filter) { if (!aFilter.accepts(message, getContext())) { return false; } } return true; } } @Override public void dispatch(MessagePublication publication, Object message, Iterable listeners){ if (passesFilter(message)) { getDelegate().dispatch(publication, message, listeners); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/HandlerInvocation.java000066400000000000000000000020171314636521700273710ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.subscription.AbstractSubscriptionContextAware; import net.engio.mbassy.subscription.SubscriptionContext; import java.util.Collection; /** * This is the base class for handler invocations that already implements all context related methods only leaving the implementation of the actual invocation mechanism to the concrete subclass. * * @author bennidi * Date: 3/29/13 */ public abstract class HandlerInvocation extends AbstractSubscriptionContextAware implements IHandlerInvocation{ public HandlerInvocation(SubscriptionContext context) { super(context); } protected final void handlePublicationError(MessagePublication publication, PublicationError error){ publication.markError(error); getContext().handleError(error); } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/IHandlerInvocation.java000066400000000000000000000023501314636521700275020ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.subscription.ISubscriptionContextAware; /** * A handler invocation encapsulates the logic that is used to invoke a single * message handler to process a given message. * * A handler invocation might come in different flavours and can be composed * of various independent invocations by means of delegation (-> decorator pattern) * * If an exception is thrown during handler invocation it is wrapped and propagated * as a publication error * * @author bennidi * Date: 11/23/12 */ public interface IHandlerInvocation extends ISubscriptionContextAware { /** * Invoke the message delivery logic of this handler * * @param handler The listener that will receive the message. This can be a reference to a method object * from the java reflection api or any other wrapper that can be used to invoke the handler * @param message The message to be delivered to the handler. This can be any object compatible with the object * type that the handler consumes */ void invoke(HANDLER handler, MESSAGE message, MessagePublication publication); } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/IMessageDispatcher.java000066400000000000000000000033041314636521700274660ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.subscription.ISubscriptionContextAware; /** * A message dispatcher provides the functionality to deliver a single message * to a set of listeners. A message dispatcher uses a message context to access * all information necessary for the message delivery. *

* The delivery of a single message to a single listener is responsibility of the * handler invocation object associated with the dispatcher. *

* Implementations if IMessageDispatcher are partially designed using decorator pattern * such that it is possible to compose different message dispatchers into dispatcher chains * to achieve more complex dispatch logic. * * @author bennidi * Date: 11/23/12 */ public interface IMessageDispatcher extends ISubscriptionContextAware { /** * Delivers the given message to the given set of listeners. * Delivery may be delayed, aborted or restricted in various ways, depending * on the configuration of the dispatcher * * @param publication The message publication that initiated the dispatch * @param message The message that should be delivered to the listeners * @param listeners The listeners that should receive the message */ void dispatch(MessagePublication publication, Object message, Iterable listeners); /** * Get the handler invocation that will be used to deliver the * message to each listener. * * @return the handler invocation that will be used to deliver the * message to each listener */ IHandlerInvocation getInvocation(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/MessageDispatcher.java000066400000000000000000000023071314636521700273570ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.subscription.AbstractSubscriptionContextAware; import net.engio.mbassy.subscription.SubscriptionContext; /** * Standard implementation for direct, unfiltered message delivery. *

* For each message delivery, this dispatcher iterates over the listeners * and uses the previously provided handler invocation to deliver the message * to each listener * * @author bennidi * Date: 11/23/12 */ public class MessageDispatcher extends AbstractSubscriptionContextAware implements IMessageDispatcher { private final IHandlerInvocation invocation; public MessageDispatcher(SubscriptionContext context, IHandlerInvocation invocation) { super(context); this.invocation = invocation; } @Override public void dispatch(final MessagePublication publication, final Object message, final Iterable listeners){ publication.markDispatched(); for (Object listener : listeners) { getInvocation().invoke(listener, message, publication); } } @Override public IHandlerInvocation getInvocation() { return invocation; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/ReflectiveHandlerInvocation.java000066400000000000000000000041111314636521700313770ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.subscription.SubscriptionContext; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Uses reflection to invoke a message handler for a given message. * * @author bennidi * Date: 11/23/12 */ public class ReflectiveHandlerInvocation extends HandlerInvocation{ public ReflectiveHandlerInvocation(SubscriptionContext context) { super(context); } /** * {@inheritDoc} */ @Override public void invoke(final Object listener, final Object message, MessagePublication publication){ final Method handler = getContext().getHandler().getMethod(); try { handler.invoke(listener, message); } catch (IllegalAccessException e) { handlePublicationError(publication, new PublicationError(e, "Error during invocation of message handler. " + "The class or method is not accessible", handler, listener, publication)); } catch (IllegalArgumentException e) { handlePublicationError(publication, new PublicationError(e, "Error during invocation of message handler. " + "Wrong arguments passed to method. Was: " + message.getClass() + "Expected: " + handler.getParameterTypes()[0], handler, listener, publication)); } catch (InvocationTargetException e) { handlePublicationError(publication, new PublicationError(e, "Error during invocation of message handler. " + "There might be an access rights problem. Do you use non public inner classes?", handler, listener, publication)); } catch (Throwable e) { handlePublicationError(publication, new PublicationError(e, "Error during invocation of message handler. " + "The handler code threw an exception", handler, listener, publication)); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/SynchronizedHandlerInvocation.java000066400000000000000000000015721314636521700317760ustar00rootroot00000000000000package net.engio.mbassy.dispatch; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.subscription.AbstractSubscriptionContextAware; /** * Synchronizes message handler invocations for all handlers that specify @Synchronized * * @author bennidi * Date: 3/31/13 */ public class SynchronizedHandlerInvocation extends AbstractSubscriptionContextAware implements IHandlerInvocation { private IHandlerInvocation delegate; public SynchronizedHandlerInvocation(IHandlerInvocation delegate) { super(delegate.getContext()); this.delegate = delegate; } /** * {@inheritDoc} */ @Override public void invoke(final Object listener, final Object message, MessagePublication publication){ synchronized (listener){ delegate.invoke(listener, message, publication); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/el/000077500000000000000000000000001314636521700235175ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/el/ElFilter.java000066400000000000000000000055531314636521700261000ustar00rootroot00000000000000package net.engio.mbassy.dispatch.el; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.listener.IMessageFilter; import net.engio.mbassy.listener.MessageHandler; import net.engio.mbassy.subscription.SubscriptionContext; import javax.el.ExpressionFactory; import javax.el.ValueExpression; /** * A filter that will use a expression from the handler annotation and * parse it as EL. *

* Accepts a message if the associated EL expression evaluates to true */ public class ElFilter implements IMessageFilter { // thread-safe initialization of EL factory singleton public static final class ExpressionFactoryHolder { // if runtime exception is thrown, this will public static final ExpressionFactory ELFactory = getELFactory(); /** * ********************************************************************** * Get an implementation of the ExpressionFactory. This uses the * Java service lookup mechanism to find a proper implementation. * If none is available we do not support EL filters. * ********************************************************************** */ private static final ExpressionFactory getELFactory() { try { return ExpressionFactory.newInstance(); } catch (RuntimeException e) { return null; } } } public static final boolean isELAvailable() { return ExpressionFactoryHolder.ELFactory != null; } public static final ExpressionFactory ELFactory() { return ExpressionFactoryHolder.ELFactory; } @Override public boolean accepts(Object message, final SubscriptionContext context) { final MessageHandler metadata = context.getHandler(); String expression = metadata.getCondition(); StandardELResolutionContext resolutionContext = new StandardELResolutionContext(message); return evalExpression(expression, resolutionContext, context, message); } private boolean evalExpression(final String expression, final StandardELResolutionContext resolutionContext, final SubscriptionContext context, final Object message) { ValueExpression ve = ELFactory().createValueExpression(resolutionContext, expression, Boolean.class); try { return (Boolean)ve.getValue(resolutionContext); } catch (Throwable exception) { PublicationError publicationError = new PublicationError(exception, "Error while evaluating EL expression on message", context) .setPublishedMessage(message); context.handleError(publicationError); return false; } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/dispatch/el/StandardELResolutionContext.java000066400000000000000000000062011314636521700317730ustar00rootroot00000000000000package net.engio.mbassy.dispatch.el; import javax.el.*; import java.lang.reflect.Method; /** * This ELContext implementation provides support for standard BeanEL resolution in conditional message handlers. * The message parameter of the message handlers is bound to 'msg' such that it can be referenced int the EL expressions. * *

 *  
 *  {@literal @}Handler(condition = "msg.type == 'onClick'")
 *  public void handle(ButtonEvent event)
 *  
 *  
*/ public class StandardELResolutionContext extends ELContext { private final ELResolver resolver; private final FunctionMapper functionMapper; private final VariableMapper variableMapper; private final Object message; public StandardELResolutionContext(Object message) { super(); this.message = message; this.functionMapper = new NoopFunctionMapper(); this.variableMapper = new MsgMapper(); // Composite resolver not necessary as the only resolution type currently supported is standard BeanEL //this.resolver = new CompositeELResolver(); this.resolver = new BeanELResolver(true); } /************************************************************************* * The resolver for the event object. * @see javax.el.ELContext#getELResolver() ************************************************************************/ @Override public ELResolver getELResolver() { return this.resolver; } /************************************************************************* * @see javax.el.ELContext#getFunctionMapper() ************************************************************************/ @Override public FunctionMapper getFunctionMapper() { return this.functionMapper; } /************************************************************************* * @see javax.el.ELContext#getVariableMapper() ************************************************************************/ @Override public VariableMapper getVariableMapper() { return this.variableMapper; } /** * This mapper resolves the variable identifies "msg" to the message * object of the current handler invocation */ private class MsgMapper extends VariableMapper { private static final String msg = "msg"; // reuse the same expression as it always resolves to the same object private final ValueExpression msgExpression = ElFilter.ELFactory().createValueExpression(message, message.getClass()); public ValueExpression resolveVariable(final String s) { // resolve 'msg' to the message object of the handler invocation return !s.equals(msg) ? null : msgExpression; } public ValueExpression setVariable(String s, ValueExpression valueExpression) { // not necessary - the mapper resolves only "msg" and nothing else return null; } } /** * This function mapper does nothing, i.e. custom EL functions are not * supported by default. It may be supported in the future to pass in * custom function mappers at bus instanciation time. */ private class NoopFunctionMapper extends FunctionMapper { public Method resolveFunction(String s, String s1) { return null; } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/000077500000000000000000000000001314636521700231455ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/Enveloped.java000066400000000000000000000013131314636521700257270ustar00rootroot00000000000000package net.engio.mbassy.listener; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Configure a handler to receive an enveloped message as a wrapper around the source * message. An enveloped message can contain any type of message * * @author bennidi * Date: 2/8/12 */ @Retention(value = RetentionPolicy.RUNTIME) @Inherited @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Enveloped { /** * The set of messages that should be dispatched to the message handler */ Class[] messages(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/Filter.java000066400000000000000000000014331314636521700252360ustar00rootroot00000000000000package net.engio.mbassy.listener; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * The filter annotation is used to add filters to message listeners. * It references a class that implements the IMessageFilter interface. * The filter will be used to check whether a message should be delivered * to the listener or not. * * @author bennidi * Date: 2/14/12 */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.ANNOTATION_TYPE}) public @interface Filter { /** * The class that implements the filter. * IMPORTANT: A filter always needs to provide a non-arg constructor */ Class value(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/Filters.java000066400000000000000000000036121314636521700254220ustar00rootroot00000000000000package net.engio.mbassy.listener; import net.engio.mbassy.subscription.SubscriptionContext; /** * A set of standard filters for common use cases. * * @author bennidi * Date: 12/12/12 */ public class Filters { /** * This filter will only accept messages of the exact same type * as specified for the handler. Subclasses (this includes interface implementations) * will be rejected. * * NOTE: The same functionality (with better performance) is achieved using {@code rejectSubtypes = true} * in the @Handler annotation */ public static final class RejectSubtypes implements IMessageFilter { @Override public boolean accepts(final Object event, final SubscriptionContext context) { final MessageHandler metadata = context.getHandler(); for (Class handledMessage : metadata.getHandledMessages()) { if (handledMessage.equals(event.getClass())) { return true; } } return false; } } /** * This filter will only accept messages that are real subtypes * of the specified message types handled by the message handler. * Example: If the handler handles Object.class the filter accepts * all objects except any direct instance of Object.class {@code new Object()} */ public static final class SubtypesOnly implements IMessageFilter{ @Override public boolean accepts(final Object message, final SubscriptionContext context) { final MessageHandler metadata = context.getHandler(); for(Class acceptedClasses : metadata.getHandledMessages()){ if(acceptedClasses.isAssignableFrom(message.getClass()) && ! acceptedClasses.equals(message.getClass())) return true; } return false; } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/Handler.java000066400000000000000000000054071314636521700253730ustar00rootroot00000000000000package net.engio.mbassy.listener; import net.engio.mbassy.dispatch.HandlerInvocation; import net.engio.mbassy.dispatch.ReflectiveHandlerInvocation; import java.lang.annotation.*; /** * Mark any method of any class(=listener) as a message handler and configure the handler * using different properties. * * @author bennidi * Date: 2/8/12 */ @Retention(value = RetentionPolicy.RUNTIME) @Inherited @Target(value = {ElementType.METHOD,ElementType.ANNOTATION_TYPE}) public @interface Handler { /** * Add any numbers of filters to the handler. All filters are evaluated before the handler * is actually invoked, which is only if all the filters accept the message. */ Filter[] filters() default {}; /** * Defines a filter condition as Expression Language. This can be used to filter the events based on * attributes of the event object. Note that the expression must resolve to either * true to allow the event or false to block it from delivery to the handler. * The message itself is available as "msg" variable. * @return the condition in EL syntax. */ String condition() default ""; /** * Define the mode in which a message is delivered to each listener. Listeners can be notified * sequentially or concurrently. */ Invoke delivery() default Invoke.Synchronously; /** * Handlers are ordered by priority and handlers with higher priority are processed before * those with lower priority, i.e. Influence the order in which different handlers that consume * the same message type are invoked. */ int priority() default 0; /** * Define whether or not the handler accepts sub types of the message type it declares in its * signature. */ boolean rejectSubtypes() default false; /** * Enable or disable the handler. Disabled handlers do not receive any messages. * This property is useful for quick changes in configuration and necessary to disable * handlers that have been declared by a superclass but do not apply to the subclass */ boolean enabled() default true; /** * Each handler call is implemented as an invocation object that implements the invocation mechanism. * The basic implementation uses reflection and is the default. It is possible though to provide a custom * invocation to add additional logic. * * Note: Providing a custom invocation will most likely reduce performance, since the JIT-Compiler * can not do some of its sophisticated byte code optimizations. * */ Class invocation() default ReflectiveHandlerInvocation.class; } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/IMessageFilter.java000066400000000000000000000030431314636521700266530ustar00rootroot00000000000000package net.engio.mbassy.listener; import net.engio.mbassy.subscription.SubscriptionContext; /** * Message filters can be used to control what messages are delivered to a specific message handler. * Filters are attached to message handler using the @Handler annotation. * If a message handler specifies filters, the filters accepts(...) method will be checked before the actual handler is invoked. * The handler will be invoked only if each filter accepted the message. * * Example: * *
 * 
 * {@literal @}Listener
 * {@literal @}Filters(Urlfilter.class)
 * public void someHandler(String message){...}
 *
 * class Urlfilter implements IMessageFilter{
 *     public boolean accepts(String message, SubscriptionContext context){
 *         return message.startsWith("http");
 *     }
 * }
 *
 * bus.post("http://www.infoq.com"); // will be delivered
 * bus.post("www.stackoverflow.com"); // will not be delivered
 *
 * 
 * 
* * NOTE: A message filter must provide a no-arg constructor!!! * * * @author bennidi * Date: 2/8/12 */ public interface IMessageFilter { /** * Check whether the message matches some criteria * * @param message The message to be handled by the handler * @param context The context object containing a description of the message handler and the bus environment * @return true: if the message matches the criteria and should be delivered to the handler * false: otherwise */ boolean accepts(M message, SubscriptionContext context); } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/IncludeFilters.java000066400000000000000000000012511314636521700267230ustar00rootroot00000000000000package net.engio.mbassy.listener; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * The include filters directive can be used to add multiple {@link Filter}s to * a single {@link ElementType#ANNOTATION_TYPE annotation type}. This allows to add * filters to message handlers with one or more user-defined annotations. * * @see Filter */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface IncludeFilters { /** * An array of filters to be used for filtering {@link Handler}s. */ Filter[] value(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/Invoke.java000066400000000000000000000002641314636521700252450ustar00rootroot00000000000000package net.engio.mbassy.listener; /** * Created with IntelliJ IDEA. * * @author bennidi * Date: 11/16/12 */ public enum Invoke { Synchronously, Asynchronously } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/Listener.java000066400000000000000000000012161314636521700255750ustar00rootroot00000000000000package net.engio.mbassy.listener; import java.lang.annotation.*; /** * * Configure how the listener is referenced in the event bus. * The bus will use either strong or weak references to its registered listeners, * depending on which reference type (@see References) is set. * * @author bennidi */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Inherited public @interface Listener { /** * BY DEFAULT, MBassador uses {@link java.lang.ref.WeakReference}. It is possible to use * strong instead. * */ References references() default References.Weak; } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/MessageHandler.java000066400000000000000000000221061314636521700266730ustar00rootroot00000000000000package net.engio.mbassy.listener; import net.engio.mbassy.common.ReflectionUtils; import net.engio.mbassy.dispatch.HandlerInvocation; import net.engio.mbassy.dispatch.el.ElFilter; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; /** * Any method in any class annotated with the @Handler annotation represents a message handler. The class that contains * the handler is called a message listener and more generally, any class containing a message handler in its class hierarchy * defines such a message listener. * * @author bennidi * Date: 11/14/12 */ public class MessageHandler { public static final class Properties{ public static final String HandlerMethod = "handler"; public static final String InvocationMode = "invocationMode"; public static final String Filter = "filter"; public static final String Condition = "condition"; public static final String Enveloped = "envelope"; public static final String HandledMessages = "messages"; public static final String IsSynchronized = "synchronized"; public static final String Listener = "listener"; public static final String AcceptSubtypes = "subtypes"; public static final String Priority = "priority"; public static final String Invocation = "invocation"; /** * Create the property map for the {@link MessageHandler} constructor using the default objects. * * @param handler The handler annotated method of the listener * @param handlerConfig The annotation that configures the handler * @param filter The set of preconfigured filters if any * @param listenerConfig The listener metadata * @return A map of properties initialized from the given parameters that will conform to the requirements of the * {@link MessageHandler} constructor. */ public static final Map Create(Method handler, Handler handlerConfig, IMessageFilter[] filter, MessageListener listenerConfig){ if(handler == null){ throw new IllegalArgumentException("The message handler configuration may not be null"); } if(filter == null){ filter = new IMessageFilter[]{}; } Enveloped enveloped = ReflectionUtils.getAnnotation( handler, Enveloped.class ); Class[] handledMessages = enveloped != null ? enveloped.messages() : handler.getParameterTypes(); handler.setAccessible(true); Map properties = new HashMap(); properties.put(HandlerMethod, handler); // add EL filter if a condition is present if(handlerConfig.condition().length() > 0){ if (!ElFilter.isELAvailable()) { throw new IllegalStateException("A handler uses an EL filter but no EL implementation is available."); } IMessageFilter[] expandedFilter = new IMessageFilter[filter.length + 1]; for(int i = 0; i < filter.length ; i++){ expandedFilter[i] = filter[i]; } expandedFilter[filter.length] = new ElFilter(); filter = expandedFilter; } properties.put(Filter, filter); properties.put(Condition, cleanEL(handlerConfig.condition())); properties.put(Priority, handlerConfig.priority()); properties.put(Invocation, handlerConfig.invocation()); properties.put(InvocationMode, handlerConfig.delivery()); properties.put(Enveloped, enveloped != null); properties.put(AcceptSubtypes, !handlerConfig.rejectSubtypes()); properties.put(Listener, listenerConfig); properties.put(IsSynchronized, ReflectionUtils.getAnnotation( handler, Synchronized.class) != null); properties.put(HandledMessages, handledMessages); return properties; } private static String cleanEL(String expression) { if (!expression.trim().startsWith("${") && !expression.trim().startsWith("#{")) { expression = "${"+expression+"}"; } return expression; } } private final Method handler; private final IMessageFilter[] filter; private final String condition; private final int priority; private final Class invocation; private final Invoke invocationMode; private final boolean isEnvelope; private final Class[] handledMessages; private final boolean acceptsSubtypes; private final MessageListener listenerConfig; private final boolean isSynchronized; public MessageHandler(Map properties){ super(); validate(properties); this.handler = (Method)properties.get(Properties.HandlerMethod); this.filter = (IMessageFilter[])properties.get(Properties.Filter); this.condition = (String)properties.get(Properties.Condition); this.priority = (Integer)properties.get(Properties.Priority); this.invocation = (Class)properties.get(Properties.Invocation); this.invocationMode = (Invoke)properties.get(Properties.InvocationMode); this.isEnvelope = (Boolean)properties.get(Properties.Enveloped); this.acceptsSubtypes = (Boolean)properties.get(Properties.AcceptSubtypes); this.listenerConfig = (MessageListener)properties.get(Properties.Listener); this.isSynchronized = (Boolean)properties.get(Properties.IsSynchronized); this.handledMessages = (Class[])properties.get(Properties.HandledMessages); } private void validate(Map properties){ // define expected types of known properties Object[][] expectedProperties = new Object[][]{ new Object[]{Properties.HandlerMethod, Method.class }, new Object[]{Properties.Priority, Integer.class }, new Object[]{Properties.Invocation, Class.class }, new Object[]{Properties.Filter, IMessageFilter[].class }, new Object[]{Properties.Condition, String.class }, new Object[]{Properties.Enveloped, Boolean.class }, new Object[]{Properties.HandledMessages, Class[].class }, new Object[]{Properties.IsSynchronized, Boolean.class }, new Object[]{Properties.Listener, MessageListener.class }, new Object[]{Properties.AcceptSubtypes, Boolean.class } }; // ensure types match for(Object[] property : expectedProperties){ if (properties.get(property[0]) == null || !((Class)property[1]).isAssignableFrom(properties.get(property[0]).getClass())) throw new IllegalArgumentException("Property " + property[0] + " was expected to be not null and of type " + property[1] + " but was: " + properties.get(property[0])); } } public
A getAnnotation(Class annotationType){ return ReflectionUtils.getAnnotation(handler,annotationType); } public boolean isSynchronized(){ return isSynchronized; } public boolean useStrongReferences(){ return listenerConfig.useStrongReferences(); } public boolean isFromListener(Class listener){ return listenerConfig.isFromListener(listener); } public boolean isAsynchronous() { return invocationMode.equals(Invoke.Asynchronously); } public boolean isFiltered() { return filter.length > 0 || (condition != null && condition.trim().length() > 0); } public int getPriority() { return priority; } public Method getMethod() { return handler; } public IMessageFilter[] getFilter() { return filter; } public String getCondition() { return this.condition; } public Class[] getHandledMessages() { return handledMessages; } public boolean isEnveloped() { return isEnvelope; } public Class getHandlerInvocation(){ return invocation; } public boolean handlesMessage(Class messageType) { for (Class handledMessage : handledMessages) { if (handledMessage.equals(messageType)) { return true; } if (handledMessage.isAssignableFrom(messageType) && acceptsSubtypes()) { return true; } } return false; } public boolean acceptsSubtypes() { return acceptsSubtypes; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/MessageListener.java000066400000000000000000000057621314636521700271140ustar00rootroot00000000000000package net.engio.mbassy.listener; import net.engio.mbassy.common.IPredicate; import net.engio.mbassy.common.ReflectionUtils; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; /** * All instances of any class defining at least one message handler @link MessageHandler are message listeners. Thus, * a message listener is any object capable of receiving messages by means of defined message handlers. * There are no restrictions about the number of allowed message handlers in a message listener. * * A message listener can be configured using the @Listener annotation but is always implicitly configured by the handler * definition it contains. * * This class is an internal representation of a message listener used to encapsulate all relevant objects * and data about that message listener, especially all its handlers. * There will be only one instance of MessageListener per message listener class and message bus instance. * * @author bennidi * Date: 12/16/12 */ public class MessageListener { public static IPredicate ForMessage(final Class messageType) { return new IPredicate() { @Override public boolean apply(MessageHandler target) { return target.handlesMessage(messageType); } }; } private ArrayList handlers = new ArrayList(); private Class listenerDefinition; private Listener listenerAnnotation; public MessageListener(Class listenerDefinition) { this.listenerDefinition = listenerDefinition; listenerAnnotation = ReflectionUtils.getAnnotation( listenerDefinition, Listener.class ); } public boolean isFromListener(Class listener){ return listenerDefinition.equals(listener); } public boolean useStrongReferences(){ return listenerAnnotation != null && listenerAnnotation.references().equals(References.Strong); } public MessageListener addHandlers(Collection c) { handlers.addAll(c); return this; } public boolean addHandler(MessageHandler messageHandler) { return handlers.add(messageHandler); } public MessageHandler[] getHandlers(){ MessageHandler[] asArray = new MessageHandler[handlers.size()]; return handlers.toArray(asArray); } // used by unit tests public List getHandlers(IPredicate filter) { List matching = new ArrayList(); for (MessageHandler handler : handlers) { if (filter.apply(handler)) { matching.add(handler); } } return matching; } // used by unit tests public boolean handles(Class messageType) { return !getHandlers(ForMessage(messageType)).isEmpty(); } public Class getListerDefinition() { return listenerDefinition; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/MetadataReader.java000066400000000000000000000141561314636521700266620ustar00rootroot00000000000000package net.engio.mbassy.listener; import net.engio.mbassy.common.IPredicate; import net.engio.mbassy.common.ReflectionUtils; import net.engio.mbassy.subscription.MessageEnvelope; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * The meta data reader is responsible for parsing and validating message handler configurations. * * @author bennidi * Date: 11/16/12 */ public class MetadataReader { // This predicate is used to find all message listeners (methods annotated with @Handler) private static final IPredicate AllMessageHandlers = new IPredicate() { @Override public boolean apply(Method target) { return ReflectionUtils.getAnnotation(target, Handler.class) != null; } }; // cache already created filter instances private final Map, IMessageFilter> filterCache = new HashMap, IMessageFilter>(); // retrieve all instances of filters associated with the given subscription private IMessageFilter[] getFilter(Method method, Handler subscription) { Filter[] filterDefinitions = collectFilters(method, subscription); if (filterDefinitions.length == 0) { return null; } IMessageFilter[] filters = new IMessageFilter[filterDefinitions.length]; int i = 0; for (Filter filterDef : filterDefinitions) { IMessageFilter filter = filterCache.get(filterDef.value()); if (filter == null) { try { filter = filterDef.value().newInstance(); filterCache.put(filterDef.value(), filter); } catch (Exception e) { throw new RuntimeException(e);// propagate as runtime exception } } filters[i] = filter; i++; } return filters; } private Filter[] collectFilters(Method method, Handler subscription) { List filters = new ArrayList(subscription.filters().length); Collections.addAll(filters, subscription.filters()); Annotation[] annotations = method.getAnnotations(); for (int i = 0; i < method.getAnnotations().length; i++) { Class annotationType = annotations[i].annotationType(); IncludeFilters repeated = annotationType.getAnnotation(IncludeFilters.class); if (repeated != null) { Collections.addAll(filters, repeated.value()); } Filter filter = annotationType.getAnnotation(Filter.class); if (filter != null) { filters.add(filter); } } return filters.toArray(new Filter[filters.size()]); } // get all listeners defined by the given class (includes // listeners defined in super classes) public MessageListener getMessageListener(Class target) { MessageListener listenerMetadata = new MessageListener(target); // get all handlers (this will include all (inherited) methods directly annotated using @Handler) Method[] allHandlers = ReflectionUtils.getMethods(AllMessageHandlers, target); final int length = allHandlers.length; Method handler; for (int i = 0; i < length; i++) { handler = allHandlers[i]; // retain only those that are at the bottom of their respective class hierarchy (deepest overriding method) if (!ReflectionUtils.containsOverridingMethod(allHandlers, handler)) { // for each handler there will be no overriding method that specifies @Handler annotation // but an overriding method does inherit the listener configuration of the overwritten method Handler handlerConfig = ReflectionUtils.getAnnotation(handler, Handler.class); if (!handlerConfig.enabled() || !isValidMessageHandler(handler)) { continue; // disabled or invalid listeners are ignored } Method overriddenHandler = ReflectionUtils.getOverridingMethod(handler, target); // if a handler is overwritten it inherits the configuration of its parent method Map handlerProperties = MessageHandler.Properties.Create(overriddenHandler == null ? handler : overriddenHandler, handlerConfig, getFilter(handler, handlerConfig), listenerMetadata); MessageHandler handlerMetadata = new MessageHandler(handlerProperties); listenerMetadata.addHandler(handlerMetadata); } } return listenerMetadata; } private boolean isValidMessageHandler(Method handler) { if (handler == null || ReflectionUtils.getAnnotation( handler, Handler.class) == null) { return false; } if (handler.getParameterTypes().length != 1) { // a messageHandler only defines one parameter (the message) System.out.println("Found no or more than one parameter in messageHandler [" + handler.getName() + "]. A messageHandler must define exactly one parameter"); return false; } Enveloped envelope = ReflectionUtils.getAnnotation( handler, Enveloped.class); if (envelope != null && !MessageEnvelope.class.isAssignableFrom(handler.getParameterTypes()[0])) { System.out.println("Message envelope configured but no subclass of MessageEnvelope found as parameter"); return false; } if (envelope != null && envelope.messages().length == 0) { System.out.println("Message envelope configured but message types defined for handler"); return false; } return true; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/References.java000066400000000000000000000002021314636521700260630ustar00rootroot00000000000000package net.engio.mbassy.listener; /** * * @author bennidi * Date: 3/29/13 */ public enum References { Strong,Weak } mbassador-1.3.1/src/main/java/net/engio/mbassy/listener/Synchronized.java000066400000000000000000000015301314636521700264660ustar00rootroot00000000000000package net.engio.mbassy.listener; import java.lang.annotation.*; /** * A handler marked with this annotation is guaranteed to be invoked in a thread-safe manner, that is, no * other running message publication will be able to invoke this or any other synchronized handler of the same * listener until the handler completed. It is equal to wrapping the handler code in a synchronized{} block. * This feature will reduce performance of message publication. Try to avoid shared mutable state whenever possible * and use immutable data instead. * * Note: Unsynchronized handlers may still be invoked concurrently with synchronized ones * * * * @author bennidi * Date: 3/31/13 */ @Retention(value = RetentionPolicy.RUNTIME) @Inherited @Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE}) public @interface Synchronized { } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/000077500000000000000000000000001314636521700240445ustar00rootroot00000000000000mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/AbstractSubscriptionContextAware.java000066400000000000000000000010021314636521700333750ustar00rootroot00000000000000package net.engio.mbassy.subscription; /** * The base implementation for subscription context aware objects (mightily obvious :) * * @author bennidi * Date: 3/1/13 */ public class AbstractSubscriptionContextAware implements ISubscriptionContextAware { private final SubscriptionContext context; public AbstractSubscriptionContextAware(SubscriptionContext context) { this.context = context; } public final SubscriptionContext getContext() { return context; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/ISubscriptionContextAware.java000066400000000000000000000006371314636521700320370ustar00rootroot00000000000000package net.engio.mbassy.subscription; /** * This interface marks components that have access to the subscription context. * * @author bennidi * Date: 3/1/13 */ public interface ISubscriptionContextAware{ /** * Get the subscription context associated with this object * * @return the subscription context associated with this object */ SubscriptionContext getContext(); } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/ISubscriptionManagerProvider.java000066400000000000000000000004541314636521700325150ustar00rootroot00000000000000package net.engio.mbassy.subscription; import net.engio.mbassy.bus.BusRuntime; import net.engio.mbassy.listener.MetadataReader; public interface ISubscriptionManagerProvider { SubscriptionManager createManager(MetadataReader reader, SubscriptionFactory factory, BusRuntime runtime); } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/MessageEnvelope.java000066400000000000000000000007131314636521700277720ustar00rootroot00000000000000package net.engio.mbassy.subscription; /** * A message envelope is used to wrap messages of arbitrary type such that a handler * my receive messages of different types. * * @author bennidi * Date: 12/12/12 */ public class MessageEnvelope { // Internal state private Object message; public MessageEnvelope(Object message) { this.message = message; } public T getMessage() { return (T) message; } } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/Subscription.java000066400000000000000000000076171314636521700274060ustar00rootroot00000000000000package net.engio.mbassy.subscription; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.dispatch.IMessageDispatcher; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; /** * A subscription is a thread-safe container that manages exactly one message handler of all registered * message listeners of the same class, i.e. all subscribed instances (excluding subclasses) of a SingleMessageHandler.class * will be referenced in the subscription created for SingleMessageHandler.class. * * There will be as many unique subscription objects per message listener class as there are message handlers * defined in the message listeners class hierarchy. * * The subscription provides functionality for message publication by means of delegation to the respective * message dispatcher. * */ public class Subscription { private final UUID id = UUID.randomUUID(); protected final Collection listeners; private final IMessageDispatcher dispatcher; private final SubscriptionContext context; private final CopyOnWriteArrayList onSubscription = new CopyOnWriteArrayList(); Subscription(SubscriptionContext context, IMessageDispatcher dispatcher, Collection listeners) { this.context = context; this.dispatcher = dispatcher; this.listeners = listeners; } /** * Check whether this subscription manages a message handler of the given listener class. */ public boolean belongsTo(Class listener){ return context.getHandler().isFromListener(listener); } /** * Check whether this subscriptions manages the given listener instance. */ public boolean contains(Object listener){ return listeners.contains(listener); } /** * Check whether this subscription manages a specific message type. */ public boolean handlesMessageType(Class messageType) { return context.getHandler().handlesMessage(messageType); } public Class[] getHandledMessageTypes(){ return context.getHandler().getHandledMessages(); } public void publish(MessagePublication publication, Object message){ if(!listeners.isEmpty()) dispatcher.dispatch(publication, message, listeners); } public int getPriority() { return context.getHandler().getPriority(); } public void subscribe(Object o) { listeners.add(o); for(Runnable callback : onSubscription.toArray(new Runnable[]{})){ callback.run(); } } public boolean unsubscribe(Object existingListener) { return listeners.remove(existingListener); } public int size() { return listeners.size(); } public Handle getHandle(){ return new Handle(); } public static final Comparator SubscriptionByPriorityDesc = new Comparator() { @Override public int compare(Subscription o1, Subscription o2) { int byPriority = ((Integer)o2.getPriority()).compareTo(o1.getPriority()); return byPriority == 0 ? o2.id.compareTo(o1.id) : byPriority; } }; /** * A handle exposes specific functionality of a subscription to be used by clients. * */ public class Handle { /** * Add a runnable that is invoked after a new listener is subscribed to the corresponding subscription. * The runnable does not receive any information on the event to prevent clients from untroducing coupling * between senders/receivers. * * @param handler The code to be run after each subscription */ void onSubscription(Runnable handler){ Subscription.this.onSubscription.add(handler); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/SubscriptionContext.java000066400000000000000000000037741314636521700307530ustar00rootroot00000000000000package net.engio.mbassy.subscription; import net.engio.mbassy.bus.BusRuntime; import net.engio.mbassy.bus.common.RuntimeProvider; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.listener.MessageHandler; import java.util.Collection; /** * The subscription context holds all (meta)data/objects that are relevant to successfully publish * a message within a subscription. A one-to-one relation between a subscription and * subscription context holds -> a subscription context is created for each distinct subscription * managed by the subscription manager. * * @author bennidi * Date: 11/23/12 */ public class SubscriptionContext implements RuntimeProvider { // the handler's metadata -> for each handler in a listener, a unique subscription context is created private final MessageHandler handler; // error handling is first-class functionality private final Collection errorHandlers; private final BusRuntime runtime; public SubscriptionContext(final BusRuntime runtime, final MessageHandler handler, final Collection errorHandlers) { this.runtime = runtime; this.handler = handler; this.errorHandlers = errorHandlers; } /** * Get the meta data that specifies the characteristics of the message handler * that is associated with this context */ public MessageHandler getHandler() { return handler; } /** * Get the error handlers registered with the enclosing bus. */ public Collection getErrorHandlers(){ return errorHandlers; } @Override public BusRuntime getRuntime() { return runtime; } public final void handleError(PublicationError error){ for (IPublicationErrorHandler errorHandler : errorHandlers) { errorHandler.handleError(error); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/SubscriptionFactory.java000066400000000000000000000072601314636521700307300ustar00rootroot00000000000000package net.engio.mbassy.subscription; import net.engio.mbassy.bus.BusRuntime; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.MessageBusException; import net.engio.mbassy.common.StrongConcurrentSet; import net.engio.mbassy.common.WeakConcurrentSet; import net.engio.mbassy.dispatch.*; import net.engio.mbassy.listener.MessageHandler; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.util.Collection; /** * The subscription factory is used to create an empty subscription for specific message handler. * The message handler's configuration is evaluated and a corresponding subscription is built. */ public class SubscriptionFactory { public Subscription createSubscription(BusRuntime runtime, MessageHandler handlerMetadata) throws MessageBusException{ try { Collection errorHandlers = runtime.get(IBusConfiguration.Properties.PublicationErrorHandlers); SubscriptionContext context = new SubscriptionContext(runtime, handlerMetadata, errorHandlers); IHandlerInvocation invocation = buildInvocationForHandler(context); IMessageDispatcher dispatcher = buildDispatcher(context, invocation); return new Subscription(context, dispatcher, handlerMetadata.useStrongReferences() ? new StrongConcurrentSet() : new WeakConcurrentSet()); } catch (MessageBusException e) { throw e; } catch (Exception e) { throw new MessageBusException(e); } } protected IHandlerInvocation buildInvocationForHandler(SubscriptionContext context) throws MessageBusException { IHandlerInvocation invocation = createBaseHandlerInvocation(context); if(context.getHandler().isSynchronized()){ invocation = new SynchronizedHandlerInvocation(invocation); } if (context.getHandler().isAsynchronous()) { invocation = new AsynchronousHandlerInvocation(invocation); } return invocation; } protected IMessageDispatcher buildDispatcher(SubscriptionContext context, IHandlerInvocation invocation) throws MessageBusException { IMessageDispatcher dispatcher = new MessageDispatcher(context, invocation); if (context.getHandler().isEnveloped()) { dispatcher = new EnvelopedMessageDispatcher(dispatcher); } if (context.getHandler().isFiltered()) { dispatcher = new FilteredMessageDispatcher(dispatcher); } return dispatcher; } protected IHandlerInvocation createBaseHandlerInvocation(SubscriptionContext context) throws MessageBusException { Class invocation = context.getHandler().getHandlerInvocation(); if(invocation.isMemberClass() && !Modifier.isStatic(invocation.getModifiers())){ throw new MessageBusException("The handler invocation must be top level class or nested STATIC inner class"); } try { Constructor constructor = invocation.getConstructor(SubscriptionContext.class); return constructor.newInstance(context); } catch (NoSuchMethodException e) { throw new MessageBusException("The provided handler invocation did not specify the necessary constructor " + invocation.getSimpleName() + "(SubscriptionContext);", e); } catch (Exception e) { throw new MessageBusException("Could not instantiate the provided handler invocation " + invocation.getSimpleName(), e); } } } mbassador-1.3.1/src/main/java/net/engio/mbassy/subscription/SubscriptionManager.java000066400000000000000000000215231314636521700306710ustar00rootroot00000000000000package net.engio.mbassy.subscription; import net.engio.mbassy.bus.BusRuntime; import net.engio.mbassy.common.ReflectionUtils; import net.engio.mbassy.common.StrongConcurrentSet; import net.engio.mbassy.listener.MessageHandler; import net.engio.mbassy.listener.MetadataReader; import java.util.*; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; /** * The subscription managers responsibility is to consistently handle and synchronize the message listener subscription process. * It provides fast lookup of existing subscriptions when another instance of an already known * listener is subscribed and takes care of creating new set of subscriptions for any unknown class that defines * message handlers. * * @author bennidi * Date: 5/11/13 */ public class SubscriptionManager { // The metadata reader that is used to inspect objects passed to the subscribe method private final MetadataReader metadataReader; // All subscriptions per message type // 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> subscriptionsPerMessage; // All subscriptions per messageHandler type // This map provides fast access for subscribing and unsubscribing // write access is synchronized and happens very infrequently // once a collection of subscriptions is stored it does not change private final Map subscriptionsPerListener; // Remember already processed classes that do not contain any message handlers private final StrongConcurrentSet nonListeners = new StrongConcurrentSet(); // This factory is used to create specialized subscriptions based on the given message handler configuration // It can be customized by implementing the getSubscriptionFactory() method private final SubscriptionFactory subscriptionFactory; // Synchronize read/write access to the subscription maps private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final BusRuntime runtime; public SubscriptionManager(MetadataReader metadataReader, SubscriptionFactory subscriptionFactory, BusRuntime runtime) { this.metadataReader = metadataReader; this.subscriptionFactory = subscriptionFactory; this.runtime = runtime; subscriptionsPerMessage = new HashMap>(256); subscriptionsPerListener = new HashMap(256); } public boolean unsubscribe(Object listener) { if (listener == null) { return false; } Subscription[] subscriptions = getSubscriptionsByListener(listener); if (subscriptions == null) { return false; } boolean isRemoved = true; for (Subscription subscription : subscriptions) { isRemoved &= subscription.unsubscribe(listener); } return isRemoved; } private Subscription[] getSubscriptionsByListener(Object listener) { Subscription[] subscriptions; ReadLock readLock = readWriteLock.readLock(); try { readLock.lock(); subscriptions = subscriptionsPerListener.get(listener.getClass()); } finally { readLock.unlock(); } return subscriptions; } public void subscribe(Object listener) { try { Class listenerClass = listener.getClass(); if (nonListeners.contains(listenerClass)) { return; // early reject of known classes that do not define message handlers } Subscription[] subscriptionsByListener = getSubscriptionsByListener(listener); // a listener is either subscribed for the first time if (subscriptionsByListener == null) { MessageHandler[] messageHandlers = metadataReader.getMessageListener(listenerClass).getHandlers(); int length = messageHandlers.length; if (length == 0) { // remember the class as non listening class if no handlers are found nonListeners.add(listenerClass); return; } subscriptionsByListener = new Subscription[length]; // it's safe to use non-concurrent collection here (read only) // create subscriptions for all detected message handlers MessageHandler messageHandler; for (int i=0; i messageType : subscription.getHandledMessageTypes()) { // associate a subscription with a message type ArrayList subscriptions2 = subscriptionsPerMessage.get(messageType); if (subscriptions2 == null) { subscriptions2 = new ArrayList(8); subscriptionsPerMessage.put(messageType, subscriptions2); } subscriptions2.add(subscription); } } subscriptionsPerListener.put(listener.getClass(), subscriptions); } // the rare case when multiple threads concurrently subscribed the same class for the first time // one will be first, all others will subscribe to the newly created subscriptions else { for (int i=0, n=subscriptionsByListener.length; i getSubscriptionsByMessageType(Class messageType) { Set subscriptions = new TreeSet(Subscription.SubscriptionByPriorityDesc); ReadLock readLock = readWriteLock.readLock(); try { readLock.lock(); Subscription subscription; ArrayList subsPerMessage = subscriptionsPerMessage.get(messageType); if (subsPerMessage != null) { subscriptions.addAll(subsPerMessage); } Class[] types = ReflectionUtils.getSuperTypes(messageType); for (int i=0, n=types.length; i subs = subscriptionsPerMessage.get(eventSuperType); if (subs != null) { for (int j = 0,m=subs.size(); j listeners = new LinkedList(); for(int i = 0; i < 1000 ; i++){ SyncListener listener = new SyncListener(); listeners.add(listener); fifoBUs.subscribe(listener); } // prepare set of messages in increasing order int[] messages = new int[1000]; for(int i = 0; i < messages.length ; i++){ messages[i] = i; } // publish in ascending order for(Integer message : messages) fifoBUs.post(message).asynchronously(); while(fifoBUs.hasPendingMessages()) pause(1000); for(SyncListener listener : listeners){ assertEquals(messages.length, listener.receivedSync.size()); for(int i=0; i < messages.length; i++){ assertEquals(messages[i], listener.receivedSync.get(i)); } } } @Test public void testSingleThreadedSyncAsyncFIFO(){ BusConfiguration asyncFIFOConfig = new BusConfiguration(); asyncFIFOConfig.addFeature(Feature.SyncPubSub.Default()); asyncFIFOConfig.addFeature(Feature.AsynchronousHandlerInvocation.Default(1, 1)); asyncFIFOConfig.addFeature(Feature.AsynchronousMessageDispatch.Default().setNumberOfMessageDispatchers(1)); IMessageBus fifoBUs = new MBassador(asyncFIFOConfig); List listeners = new LinkedList(); for(int i = 0; i < 1000 ; i++){ SyncAsyncListener listener = new SyncAsyncListener(); listeners.add(listener); fifoBUs.subscribe(listener); } // prepare set of messages in increasing order int[] messages = new int[1000]; for(int i = 0; i < messages.length ; i++){ messages[i] = i; } IMessagePublication publication = null; // publish in ascending order for(Integer message : messages) publication = fifoBUs.post(message).asynchronously(); while(fifoBUs.hasPendingMessages() && ! publication.isFinished()) pause(200); // Check the handlers processing status // Define timeframe in which processing should be finished // If not then an error is assumed long timeElapsed = 0; long timeOut = 30000; // 30 seconds long begin = System.currentTimeMillis(); for(SyncAsyncListener listener : listeners){ boolean successful = true; successful &= messages.length == listener.receivedSync.size(); successful &= listener.receivedSync.size() ==listener.receivedAsync.size(); for(int i=0; i < listener.receivedAsync.size(); i++){ successful &= messages[i] == listener.receivedSync.get(i); // sync and async in same order successful &= listener.receivedSync.get(i) == listener.receivedAsync.get(i); } if(successful) break; timeElapsed = System.currentTimeMillis() - begin; } if(timeElapsed >= timeOut) Assert.fail("Processing of handlers unfinished after timeout"); } public static class SyncListener { private List receivedSync = new LinkedList(); @Handler public void handleSync(Integer message){ receivedSync.add(message); } } public static class SyncAsyncListener { private List receivedSync = new LinkedList(); private List receivedAsync = new LinkedList(); @Handler public void handleSync(Integer message){ receivedSync.add(message); } @Handler(delivery = Invoke.Asynchronously) public void handleASync(Integer message){ receivedAsync.add(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/BusConstructionTest.java000066400000000000000000000055551314636521700262340ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.ConfigurationError; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.common.AssertSupport; import org.junit.Test; /** * Testing different ways of construction * * @author bennidi * Date: 13.09.15 */ public class BusConstructionTest extends AssertSupport { @Test public void testMBassadorDefaultConstructor(){ assertNotNull(new MBassador()); } @Test(expected = ConfigurationError.class) public void testEmptyBusConfiguration(){ new MBassador(new BusConfiguration()); } @Test(expected = ConfigurationError.class) public void testMissingMessageDispatch(){ assertNotNull(new MBassador(new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addPublicationErrorHandler(new IPublicationErrorHandler.ConsoleLogger()) .setProperty(IBusConfiguration.Properties.BusId, "global bus"))); } @Test(expected = ConfigurationError.class) public void testMissingPubSub(){ assertNotNull(new MBassador(new BusConfiguration() .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()) .addPublicationErrorHandler(new IPublicationErrorHandler.ConsoleLogger()) .setProperty(IBusConfiguration.Properties.BusId, "global bus"))); } @Test(expected = ConfigurationError.class) public void testMissingAsyncInvocation(){ assertNotNull(new MBassador(new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()) .addPublicationErrorHandler(new IPublicationErrorHandler.ConsoleLogger()) .setProperty(IBusConfiguration.Properties.BusId, "global bus"))); } @Test public void testValidBusConfiguration(){ MBassador bus = new MBassador(new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()) .addPublicationErrorHandler(new IPublicationErrorHandler.ConsoleLogger()) .setProperty(IBusConfiguration.Properties.BusId, "global bus")); assertNotNull(bus); assertEquals(1, bus.getRegisteredErrorHandlers().size()); System.out.println(bus.toString()); assertTrue(bus.toString().contains("global bus")); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/ConcurrentSetTest.java000066400000000000000000000347671314636521700256750ustar00rootroot00000000000000package net.engio.mbassy; import junit.framework.Assert; import net.engio.mbassy.common.AssertSupport; import net.engio.mbassy.common.ConcurrentExecutor; import org.junit.Before; import org.junit.Test; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; /** * This test ensures the correct behaviour of the set implementation that is the building * block of the subscription implementations used by the Mbassador message bus. *

* It should behave exactly like other set implementations do and as such all tests are based * on comparing the outcome of sequence of operations applied to a standard set implementation * and the concurrent set. * * @author bennidi * Date: 11/12/12 */ public abstract class ConcurrentSetTest extends AssertSupport { // Shared state protected final int numberOfElements = 100000; protected final int numberOfThreads = 50; // needed to avoid premature garbage collection for weakly referenced listeners protected Set gcProtector = new HashSet(); @Before public void beforeTest(){ super.beforeTest(); gcProtector = new HashSet(); } protected abstract Collection createSet(); @Test public void testAddAll() { final Collection testSet = createSet(); final List notFound = new CopyOnWriteArrayList(); // insert all elements (containing duplicates) into the set final Random rand = new Random(); ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { final List source = new LinkedList(); for (int i = 0; i < numberOfElements; i++) { source.add(rand.nextDouble()); } testSet.addAll(source); for (Number src : source) { if(!testSet.contains(src)){ notFound.add(src); } } } }, numberOfThreads); // check that the control set and the test set contain the exact same elements assertEquals(notFound.size(), 0); } @Test public void testAdd() { final Collection testSet = createSet(); final List notFound = new CopyOnWriteArrayList(); final Random rand = new Random(); // insert all elements (containing duplicates) into the set ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { final List source = new LinkedList(); for (int i = 0; i < numberOfElements; i++) { source.add(rand.nextDouble()); } for (Number src : source) { testSet.add(src); if(!testSet.contains(src)){ notFound.add(src); } } } }, numberOfThreads); // check that the control set and the test set contain the exact same elements assertEquals(notFound.size(), 0); } @Test public void testUniqueness() { final LinkedList duplicates = new LinkedList(); final HashSet distinct = new HashSet(); final Collection testSet = createSet(); Random rand = new Random(); // getAll set of distinct objects and list of duplicates Object candidate = new Object(); for (int i = 0; i < numberOfElements; i++) { if (rand.nextInt() % 3 == 0) { candidate = new Object(); } duplicates.add(candidate); distinct.add(candidate); } // insert all elements (containing duplicates) into the set ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { for (Object src : duplicates) { testSet.add(src); } } }, numberOfThreads); // check that the control set and the test set contain the exact same elements assertEquals(distinct.size(), testSet.size()); for (Object uniqueObject : distinct) { assertTrue(testSet.contains(uniqueObject)); } } @Test() public void testIterationWithConcurrentRemoval() { final Collection testSet = createSet(); final Random rand = new Random(); for (int i = 0; i < numberOfElements; i++) { AtomicInteger element = new AtomicInteger(); testSet.add(element); gcProtector.add(element); } Runnable incrementer = new Runnable() { @Override public void run() { while(testSet.size() > 100){ for(AtomicInteger element : testSet) element.incrementAndGet(); } } }; Runnable remover = new Runnable() { @Override public void run() { while(testSet.size() > 100){ for(AtomicInteger element : testSet) if(rand.nextInt() % 3 == 0 && testSet.size() > 100) testSet.remove(element); } } }; ConcurrentExecutor.runConcurrent(20, incrementer, incrementer, remover); Set counts = new HashSet(); for (AtomicInteger count : testSet) { counts.add(count.get()); } // all atomic integers should have been visited by the the incrementer // the same number of times // in other words: they have either been removed at some point or incremented in each // iteration such that all remaining atomic integers must share the same value assertEquals(1, counts.size()); } @Test public void testRandomRemoval() { final HashSet source = new HashSet(); final HashSet toRemove = new HashSet(); final Collection testSet = createSet(); // getAll set of distinct objects and mark a subset of those for removal for (int i = 0; i < numberOfElements; i++) { Object candidate = new Object(); source.add(candidate); if (i % 3 == 0) { toRemove.add(candidate); } } // getAll the test set from the set of candidates ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { for (Object src : source) { testSet.add(src); } } }, numberOfThreads); // remove all candidates that have previously been marked for removal from the test set ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { for (Object src : toRemove) { testSet.remove(src); } } }, numberOfThreads); // ensure that the test set does not contain any of the elements that have been removed from it for (Object tar : testSet) { assertTrue(!toRemove.contains(tar)); } // ensure that the test set still contains all objects from the source set that have not been marked // for removal assertEquals(source.size() - toRemove.size(), testSet.size()); for (Object src : source) { if (!toRemove.contains(src)) assertTrue(testSet.contains(src)); } } @Test public void testRemovalOfHead() { final HashSet source = new HashSet(); final HashSet toRemove = new HashSet(); final Collection testSet = createSet(); // getAll set of candidates and mark subset for removal for (int i = 0; i < numberOfElements; i++) { Object candidate = new Object(); source.add(candidate); if (i % 3 == 0) { toRemove.add(candidate); } } // getAll test set by adding the candidates // and subsequently removing those marked for removal ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { for (Object src : source) { testSet.add(src); if (toRemove.contains(src)) testSet.remove(src); } } }, numberOfThreads); // ensure that the test set does not contain any of the elements that have been removed from it for (Object tar : testSet) { Assert.assertTrue(!toRemove.contains(tar)); } // ensure that the test set still contains all objects from the source set that have not been marked // for removal assertEquals(source.size() - toRemove.size(), testSet.size()); for (Object src : source) { if (!toRemove.contains(src)) assertTrue(testSet.contains(src)); } } @Test public void testCompleteRemoval() { final HashSet source = new HashSet(); final Collection testSet = createSet(); // getAll set of candidates and mark subset for removal for (int i = 0; i < numberOfElements; i++) { Object candidate = new Object(); source.add(candidate); testSet.add(candidate); } // getAll test set by adding the candidates // and subsequently removing those marked for removal ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { for (Object src : source) { testSet.remove(src); } } }, numberOfThreads); // ensure that the test set still contains all objects from the source set that have not been marked // for removal assertEquals(0, testSet.size()); for(Object src : source){ assertFalse(testSet.contains(src)); } } @Test public void testRemovalViaIterator() { final HashSet source = new HashSet(); final Collection setUnderTest = createSet(); // getAll set of candidates and mark subset for removal for (int i = 0; i < numberOfElements; i++) { Object candidate = new Object(); source.add(candidate); setUnderTest.add(candidate); } // getAll test set by adding the candidates // and subsequently removing those marked for removal ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { Iterator iterator = setUnderTest.iterator(); while(iterator.hasNext()){ iterator.remove(); } } }, numberOfThreads); // ensure that the test set still contains all objects from the source set that have not been marked // for removal assertEquals(0, setUnderTest.size()); for(Object src : source){ assertFalse(setUnderTest.contains(src)); } } /** * In this test HashMap will cross capacity threshold multiple times in * different directions which will trigger rehashing. Because rehashing * requires modification of Entry class for all hash map entries some keys * may temporarily disappear from the map. *

* For more information please take a look at transfer method in HashMap. * * Thanks to Ivan Koblik (http://koblik.blogspot.com) for contributing initial code and idea */ @Test public void testConcurrentAddRemove() { final Collection testSet = createSet(); // a set of unique integers that will stay permanently in the test set final List permanentObjects = new ArrayList(); // a set of objects that will be added and removed at random to the test set to force rehashing final List volatileObjects = new ArrayList(); permanentObjects.addAll(createWithRandomIntegers(80, null)); volatileObjects.addAll(createWithRandomIntegers(10000, permanentObjects)); final CopyOnWriteArraySet missing = new CopyOnWriteArraySet(); final int mutatorThreshold = 1000; // Add elements that will not be touched by the constantly running mutating thread for (Object permanent : permanentObjects) { testSet.add(permanent); } // Adds and removes items // thus forcing constant rehashing of the backing hashtable Runnable rehasher = new Runnable() { public void run() { Random rand = new Random(); for(int times = 0; times < 1000 ; times++){ HashSet elements = new HashSet(mutatorThreshold); for (int i = 0; i < mutatorThreshold; i++) { Object volatileObject = volatileObjects.get(Math.abs(rand.nextInt()) % volatileObjects.size()); testSet.add(volatileObject); elements.add(volatileObject); } for (Object volObj : elements) { testSet.remove(volObj); } } }; }; Runnable lookup = new Runnable() { @Override public void run() { for (int i = 0; i < 10000; i++) { for (Object permanent : permanentObjects) { // permanent items are never touched, // --> set.contains(j) should always return true if(!testSet.contains(permanent)) missing.add(permanent); } } } }; ConcurrentExecutor.runConcurrent(rehasher, lookup, lookup, lookup); assertTrue("There where items temporarily unavailable: " + missing.size(), missing.size() == 0); } public Set createWithRandomIntegers(int size, List excluding){ if(excluding == null) excluding = new ArrayList(); Set result = new HashSet(size); Random rand = new Random(); while(result.size() < size){ result.add(rand.nextInt()); } for(Integer excluded : excluding) result.remove(excluded); return result; } } mbassador-1.3.1/src/test/java/net/engio/mbassy/ConditionalHandlerTest.java000066400000000000000000000107601314636521700266230ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.common.MessageBusTest; import net.engio.mbassy.listener.Enveloped; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.subscription.MessageEnvelope; import org.junit.Test; import java.util.HashSet; import java.util.Set; /***************************************************************************** * Some unit tests for the "condition" filter. ****************************************************************************/ public class ConditionalHandlerTest extends MessageBusTest { @Test public void testSimpleStringCondition(){ MBassador bus = createBus(SyncAsync()); bus.subscribe(new ConditionalMessageListener()); TestEvent message = new TestEvent("TEST", 0); bus.publish(message); assertTrue(message.wasHandledBy("handleTypeMessage", "handleEnvelopedMessage")); assertFalse(message.wasHandledBy("handleInvalidEL")); } @Test public void testSimpleNumberCondition(){ MBassador bus = createBus(SyncAsync()); bus.subscribe(new ConditionalMessageListener()); TestEvent message = new TestEvent("", 5); bus.publish(message); assertTrue(message.wasHandledBy("handleSizeMessage")); assertFalse(message.wasHandledBy("handleInvalidEL")); } @Test public void testHandleCombinedEL(){ MBassador bus = createBus(SyncAsync()); bus.subscribe(new ConditionalMessageListener()); TestEvent message = new TestEvent("", 3); bus.publish(message); assertTrue(message.wasHandledBy("handleCombinedEL")); assertFalse(message.wasHandledBy("handleInvalidEL")); } @Test public void testNotMatchingAnyCondition(){ MBassador bus = createBus(SyncAsync()); bus.subscribe(new ConditionalMessageListener()); TestEvent message = new TestEvent("", 0); bus.publish(message); assertTrue(message.handledBy.isEmpty()); } @Test public void testHandleMethodAccessEL(){ MBassador bus = createBus(SyncAsync()); bus.subscribe(new ConditionalMessageListener()); TestEvent message = new TestEvent("XYZ", 1); bus.publish(message); assertTrue(message.wasHandledBy("handleMethodAccessEL")); assertFalse(message.wasHandledBy("handleInvalidEL")); } public static class TestEvent { private Set handledBy = new HashSet(); private String type; private int size; public TestEvent(String type, int size) { super(); this.type = type; this.size = size; } public String getType() { return type; } public int getSize() { return size; } public boolean wasHandledBy(String ...handlers){ for(String handler : handlers){ if (!handledBy.contains(handler)) return false; } return true; } public void handledBy(String handler){ handledBy.add(handler); } } @Listener(references = References.Strong) public static class ConditionalMessageListener { @Handler(condition = "msg.type == 'TEST'") public void handleTypeMessage(TestEvent message) { message.handledBy("handleTypeMessage"); } @Handler(condition = "msg.size > 4") public void handleSizeMessage(TestEvent message) { message.handledBy("handleSizeMessage"); } @Handler(condition = "msg.foo > 4") public void handleInvalidEL(TestEvent message) { message.handledBy("handleInvalidEL"); } @Handler(condition = "msg.size > 2 && msg.size < 4") public void handleCombinedEL(TestEvent message) { message.handledBy( "handleCombinedEL"); } @Handler(condition = "msg.getType().equals('XYZ') && msg.getSize() == 1") public void handleMethodAccessEL(TestEvent message) { message.handledBy("handleMethodAccessEL"); } @Handler(condition = "msg.type == 'TEST'") @Enveloped(messages = {TestEvent.class, Object.class}) public void handleEnvelopedMessage(MessageEnvelope envelope) { envelope.getMessage().handledBy("handleEnvelopedMessage"); } } public static IBusConfiguration SyncAsync() { return MessageBusTest.SyncAsync(false) .addPublicationErrorHandler(new EmptyErrorHandler()); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/CustomHandlerAnnotationTest.java000066400000000000000000000104341314636521700276630ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.common.MessageBusTest; import net.engio.mbassy.listener.*; import net.engio.mbassy.subscription.MessageEnvelope; import net.engio.mbassy.subscription.SubscriptionContext; import org.junit.Test; import java.lang.annotation.*; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * Tests a custom handler annotation with a @Handler meta annotation and a default filter. */ public class CustomHandlerAnnotationTest extends MessageBusTest { /** * Handler annotation that adds a default filter on the NamedMessage. * Enveloped is in no way required, but simply added to test a meta enveloped annotation. */ @Retention(value = RetentionPolicy.RUNTIME) @Inherited @Handler(filters = { @Filter(NamedMessageFilter.class) }) @Synchronized @Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @interface NamedMessageHandler { /** * @return The message names supported. */ String[] value(); } /** * Handler annotation that adds a default filter on the NamedMessage. * Enveloped is in no way required, but simply added to test a meta enveloped annotation. */ @Retention(value = RetentionPolicy.RUNTIME) @Inherited @NamedMessageHandler("messageThree") @interface MessageThree {} /** * Test enveloped meta annotation. */ @Retention(value = RetentionPolicy.RUNTIME) @Target(value = { ElementType.METHOD, ElementType.ANNOTATION_TYPE }) @Inherited @Handler(filters = { @Filter(NamedMessageFilter.class) }) @Enveloped(messages = NamedMessage.class) @interface EnvelopedNamedMessageHandler { /** * @return The message names supported. */ String[] value(); } /** * Searches for a NamedMessageHandler annotation on the handler method. * The annotation specifies the supported message names. */ public static class NamedMessageFilter implements IMessageFilter { @Override public boolean accepts( NamedMessage message, SubscriptionContext context ) { MessageHandler handler = context.getHandler(); NamedMessageHandler namedMessageHandler = handler.getAnnotation(NamedMessageHandler.class); if ( namedMessageHandler != null ) { return Arrays.asList( namedMessageHandler.value() ).contains( message.getName() ); } EnvelopedNamedMessageHandler envelopedHandler = handler.getAnnotation(EnvelopedNamedMessageHandler.class); return envelopedHandler != null && Arrays.asList( envelopedHandler.value() ).contains( message.getName() ); } } static class NamedMessage { private String name; NamedMessage( String name ) { this.name = name; } public String getName() { return name; } } static class NamedMessageListener { final Set handledByOne = new HashSet(); final Set handledByTwo = new HashSet(); final Set handledByThree = new HashSet(); @NamedMessageHandler({ "messageOne", "messageTwo" }) void handlerOne( NamedMessage message ) { handledByOne.add( message ); } @EnvelopedNamedMessageHandler({ "messageTwo", "messageThree" }) void handlerTwo( MessageEnvelope envelope ) { handledByTwo.add( (NamedMessage) envelope.getMessage() ); } @MessageThree void handlerThree( NamedMessage message ) { handledByThree.add( message ); } } @Test public void testMetaHandlerFiltering() { MBassador bus = createBus(SyncAsync()); NamedMessageListener listener = new NamedMessageListener(); bus.subscribe( listener ); NamedMessage messageOne = new NamedMessage( "messageOne" ); NamedMessage messageTwo = new NamedMessage( "messageTwo" ); NamedMessage messageThree = new NamedMessage( "messageThree" ); bus.publish( messageOne ); bus.publish( messageTwo ); bus.publish( messageThree ); assertEquals(2, listener.handledByOne.size()); assertTrue( listener.handledByOne.contains( messageOne ) ); assertTrue(listener.handledByOne.contains(messageTwo)); assertEquals(2, listener.handledByTwo.size()); assertTrue( listener.handledByTwo.contains( messageTwo ) ); assertTrue( listener.handledByTwo.contains( messageThree ) ); assertEquals(1, listener.handledByThree.size()); assertTrue( listener.handledByThree.contains( messageThree ) ); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/DeadMessageTest.java000066400000000000000000000105111314636521700252160ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.common.DeadMessage; import net.engio.mbassy.common.ConcurrentExecutor; import net.engio.mbassy.common.ListenerFactory; import net.engio.mbassy.common.MessageBusTest; import net.engio.mbassy.common.TestUtil; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listeners.IMessageListener; import net.engio.mbassy.listeners.MessagesTypeListener; import net.engio.mbassy.listeners.ObjectListener; import org.junit.Before; import org.junit.Test; import java.util.concurrent.atomic.AtomicInteger; /** * Verify correct behaviour in case of message publications that do not have any matching subscriptions * * @author bennidi * Date: 1/18/13 */ public class DeadMessageTest extends MessageBusTest{ @Before public void beforeTest(){ DeadMessagHandler.deadMessages.set(0); } @Test public void testDeadMessage(){ final MBassador bus = createBus(SyncAsync()); ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, IMessageListener.DefaultListener.class) .create(InstancesPerListener, IMessageListener.AsyncListener.class) .create(InstancesPerListener, IMessageListener.DisabledListener.class) .create(InstancesPerListener, MessagesTypeListener.DefaultListener.class) .create(InstancesPerListener, MessagesTypeListener.AsyncListener.class) .create(InstancesPerListener, MessagesTypeListener.DisabledListener.class) .create(InstancesPerListener, DeadMessagHandler.class) .create(InstancesPerListener, Object.class); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, listeners), ConcurrentUnits); Runnable publishUnhandledMessage = new Runnable() { @Override public void run() { for(int i=0; i < IterationsPerThread; i++){ int variation = i % 3; switch (variation){ case 0:bus.publish(new Object());break; case 1:bus.publish(i);break; case 2:bus.publish(String.valueOf(i));break; } } } }; ConcurrentExecutor.runConcurrent(publishUnhandledMessage, ConcurrentUnits); assertEquals(InstancesPerListener * IterationsPerThread * ConcurrentUnits, DeadMessagHandler.deadMessages.get()); } @Test public void testUnsubscribingAllListeners() { final MBassador bus = createBus(SyncAsync()); ListenerFactory deadMessageListener = new ListenerFactory() .create(InstancesPerListener, DeadMessagHandler.class) .create(InstancesPerListener, Object.class); ListenerFactory objectListener = new ListenerFactory() .create(InstancesPerListener, ObjectListener.class); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, deadMessageListener), ConcurrentUnits); // Only dead message handlers available bus.post(new Object()).now(); // The message should be caught as dead message since there are no subscribed listeners assertEquals(InstancesPerListener, DeadMessagHandler.deadMessages.get()); // Clear deadmessage for future tests DeadMessagHandler.deadMessages.set(0); // Add object listeners and publish again ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, objectListener), ConcurrentUnits); bus.post(new Object()).now(); // verify that no dead message events were produced assertEquals(0, DeadMessagHandler.deadMessages.get()); // Unsubscribe all object listeners ConcurrentExecutor.runConcurrent(TestUtil.unsubscriber(bus, objectListener), ConcurrentUnits); // Only dead message handlers available bus.post(new Object()).now(); // The message should be caught, as it's the only listener assertEquals(InstancesPerListener, DeadMessagHandler.deadMessages.get()); } public static class DeadMessagHandler { private static final AtomicInteger deadMessages = new AtomicInteger(0); @Handler public void handle(DeadMessage message){ deadMessages.incrementAndGet(); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/FilterTest.java000066400000000000000000000124321314636521700243050ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.common.DeadMessage; import net.engio.mbassy.bus.common.FilteredMessage; import net.engio.mbassy.common.ListenerFactory; import net.engio.mbassy.common.MessageBusTest; import net.engio.mbassy.common.TestUtil; import net.engio.mbassy.listener.*; import net.engio.mbassy.messages.SubTestMessage; import net.engio.mbassy.messages.TestMessage; import net.engio.mbassy.subscription.SubscriptionContext; import org.junit.Test; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Testing of filter functionality * * @author bennidi * Date: 11/26/12 */ public class FilterTest extends MessageBusTest { private static final AtomicInteger FilteredEventCounter = new AtomicInteger(0); private static final AtomicInteger DeadEventCounter = new AtomicInteger(0); @Test public void testSubclassFilter() throws Exception { FilteredEventCounter.set(0); DeadEventCounter.set(0); MBassador bus = createBus(SyncAsync()); ListenerFactory listenerFactory = new ListenerFactory() .create(100, FilteredMessageListener.class); List listeners = listenerFactory.getAll(); // this will subscribe the listeners concurrently to the bus TestUtil.setup(bus, listeners, 10); TestMessage message = new TestMessage(); TestMessage subTestMessage = new SubTestMessage(); bus.post(message).now(); bus.post(subTestMessage).now(); assertEquals(100, message.counter.get()); assertEquals(0, subTestMessage.counter.get()); assertEquals(100, FilteredEventCounter.get()); } @Test public void testFilteredFilteredEvent() throws Exception { FilteredEventCounter.set(0); DeadEventCounter.set(0); MBassador bus = createBus(SyncAsync()); ListenerFactory listenerFactory = new ListenerFactory() .create(100, FilteredMessageListener.class); List listeners = listenerFactory.getAll(); // this will subscribe the listeners concurrently to the bus TestUtil.setup(bus, listeners, 10); bus.post(new Object()).now(); bus.post(new SubTestMessage()).now(); assertEquals(100, FilteredEventCounter.get()); // the SubTestMessage should have been republished as a filtered event assertEquals(100, DeadEventCounter.get()); // Object.class was filtered and the fil } public static class FilteredMessageListener{ // NOTE: Use rejectSubtypes property of @Handler to achieve the same functionality but with better performance // and more concise syntax @Handler(filters = {@Filter(Filters.RejectSubtypes.class)}) public void handleTestMessage(TestMessage message){ message.counter.incrementAndGet(); } // FilteredEvents that contain messages of class Object will be filtered (again) and should cause a DeadEvent to be thrown @Handler @RejectFiltered public void handleFilteredEvent(FilteredMessage filtered){ FilteredEventCounter.incrementAndGet(); } // will cause republication of a FilteredEvent @Handler @RejectAll public void handleNone(Object any){ FilteredEventCounter.incrementAndGet(); } // will cause republication of a FilteredEvent @Handler public void handleDead(DeadMessage dead){ DeadEventCounter.incrementAndGet(); } } @Test public void testSubtypesOnly(){ MBassador bus = createBus(SyncAsync()); ListenerFactory listeners = new ListenerFactory() .create(100, TestMessageHandler.class); // this will subscribe the listeners concurrently to the bus TestUtil.setup(bus, listeners, 10); TestMessage supertype = new TestMessage(); TestMessage subtype = new SubTestMessage(); bus.publish(supertype); bus.publish(subtype); assertEquals(100, subtype.counter.get()); assertEquals(0, supertype.counter.get()); } public static class TestMessageHandler{ @Handler(filters = @Filter(Filters.SubtypesOnly.class)) public void handle(TestMessage message){ message.counter.incrementAndGet(); } } public static class RejectFilteredObjectsFilter implements IMessageFilter{ @Override public boolean accepts(Object message, SubscriptionContext context) { if(message.getClass().equals(FilteredMessage.class) && ((FilteredMessage)message).getMessage().getClass().equals(Object.class)){ return false; } return true; } } public static final class RejectAllFilter implements IMessageFilter { @Override public boolean accepts(Object event, SubscriptionContext context) { return false; } } @Filter(RejectFilteredObjectsFilter.class) @Retention(RetentionPolicy.RUNTIME) public @interface RejectFiltered { } @IncludeFilters({@Filter(RejectAllFilter.class)}) @Retention(RetentionPolicy.RUNTIME) public @interface RejectAll { } } mbassador-1.3.1/src/test/java/net/engio/mbassy/MetadataReaderTest.java000066400000000000000000000146751314636521700257360ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.common.AssertSupport; import net.engio.mbassy.listener.MessageListener; import net.engio.mbassy.listeners.SimpleHandler; import org.junit.Test; import net.engio.mbassy.listener.Enveloped; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.MetadataReader; import net.engio.mbassy.subscription.MessageEnvelope; import java.io.BufferedReader; import java.util.HashMap; import java.util.List; import java.util.Map; import static net.engio.mbassy.listener.MessageListener.ForMessage; /** * * @author bennidi * Date: 12/16/12 */ public class MetadataReaderTest extends AssertSupport { private MetadataReader reader = new MetadataReader(); @Test public void testListenerWithoutInheritance() { MessageListener listener = reader.getMessageListener(MessageListener1.class); ListenerValidator validator = new ListenerValidator() .expectHandlers(2, String.class) .expectHandlers(2, Object.class) .expectHandlers(1, BufferedReader.class); validator.check(listener); } /* public void testInterfaced() { MessageListener listener = reader.getMessageListener(InterfacedListener.class); ListenerValidator validator = new ListenerValidator() .expectHandlers(1, Object.class); validator.check(listener); } WIP */ @Test public void testListenerWithInheritance() { MessageListener listener = reader.getMessageListener(MessageListener2.class); ListenerValidator validator = new ListenerValidator() .expectHandlers(2, String.class) .expectHandlers(2, Object.class) .expectHandlers(1, BufferedReader.class); validator.check(listener); } @Test public void testListenerWithInheritanceOverriding() { MessageListener listener = reader.getMessageListener(MessageListener3.class); ListenerValidator validator = new ListenerValidator() .expectHandlers(0, String.class) .expectHandlers(2, Object.class) .expectHandlers(0, BufferedReader.class); validator.check(listener); } @Test public void testEnveloped() { MessageListener listener = reader.getMessageListener(EnvelopedListener.class); ListenerValidator validator = new ListenerValidator() .expectHandlers(1, String.class) .expectHandlers(2, Integer.class) .expectHandlers(2, Long.class) .expectHandlers(1, Double.class) .expectHandlers(1, Number.class) .expectHandlers(0, List.class); validator.check(listener); } @Test public void testEnvelopedSubclass() { MessageListener listener = reader.getMessageListener(EnvelopedListenerSubclass.class); ListenerValidator validator = new ListenerValidator() .expectHandlers(1, String.class) .expectHandlers(2, Integer.class) .expectHandlers(1, Long.class) .expectHandlers(0, Double.class) .expectHandlers(0, Number.class); validator.check(listener); } @Test public void testAnonymousListener() { SimpleHandler anonymousSimpleHandler = new SimpleHandler() { @Override @Handler public void onMessage(Object msg) { // nop } }; MessageListener listener = reader.getMessageListener(anonymousSimpleHandler.getClass()); ListenerValidator validator = new ListenerValidator() .expectHandlers(1, Object.class); validator.check(listener); } // Define and assert expectations on handlers in a listener private class ListenerValidator { private Map, Integer> handlers = new HashMap, Integer>(); public ListenerValidator expectHandlers(Integer count, Class messageType){ handlers.put(messageType, count); return this; } public void check(MessageListener listener){ for(Map.Entry, Integer> expectedHandler: handlers.entrySet()){ if(expectedHandler.getValue() > 0){ assertTrue(listener.handles(expectedHandler.getKey())); } else{ assertFalse(listener.handles(expectedHandler.getKey())); } assertEquals(expectedHandler.getValue(), listener.getHandlers(ForMessage(expectedHandler.getKey())).size()); } } } // a simple event listener public class MessageListener1 { @Handler(rejectSubtypes = true) public void handleObject(Object o) { } @Handler public void handleAny(Object o) { } @Handler public void handleString(String s) { } } // the same handlers as its super class public class MessageListener2 extends MessageListener1 { // redefine handler implementation (not configuration) public void handleString(String s) { } } public class MessageListener3 extends MessageListener2 { // narrow the handler @Handler(rejectSubtypes = true) public void handleAny(Object o) { } @Handler(enabled = false) public void handleString(String s) { } } public class EnvelopedListener{ @Handler(rejectSubtypes = true) @Enveloped(messages = {String.class, Integer.class, Long.class}) public void handleEnveloped(MessageEnvelope o) { } @Handler @Enveloped(messages = Number.class) public void handleEnveloped2(MessageEnvelope o) { } } public class EnvelopedListenerSubclass extends EnvelopedListener{ // narrow to integer @Handler @Enveloped(messages = Integer.class) public void handleEnveloped2(MessageEnvelope o) { } } public static interface ListenerInterface{ @Handler @Enveloped(messages = Object.class) void handle(MessageEnvelope envelope); } public class InterfacedListener implements ListenerInterface{ @Override public void handle(MessageEnvelope envelope) { // } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/MethodDispatchTest.java000066400000000000000000000064261314636521700257660ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.common.IMessageBus; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.common.MessageBusTest; import net.engio.mbassy.listener.Handler; import org.junit.Assert; import org.junit.Test; import java.util.concurrent.atomic.AtomicInteger; /** * Very simple test to verify dispatch to correct message handler * * @author bennidi * Date: 1/17/13 */ public class MethodDispatchTest extends MessageBusTest { private boolean listener1Called = false; private boolean listener2Called = false; // a simple event listener public class EventListener1 { @Handler public void handleString(String s) { listener1Called = true; } } // the same handlers as its super class public class EventListener2 extends EventListener1 { // redefine handler implementation (not configuration) public void handleString(String s) { listener2Called = true; } } @Test public void testDispatchWithSequentialSubscription() { IMessageBus bus = createBus(SyncAsync()); EventListener2 listener2 = new EventListener2(); bus.subscribe(listener2); bus.post("only one listener registered").now(); assertTrue(listener2Called); assertFalse(listener1Called); EventListener1 listener1 = new EventListener1(); bus.subscribe(listener1); bus.post("second listener registered and called").now(); assertTrue(listener1Called); } @Test public void testAsyncDispatchAfterExceptionInErrorHandler() throws InterruptedException { IMessageBus bus = createBus(SyncAsync(false /*configures an error handler that throws exception*/).addFeature(Feature.AsynchronousMessageDispatch.Default().setNumberOfMessageDispatchers(1))); final AtomicInteger msgHandlerCounter = new AtomicInteger(0); bus.subscribe(new Object() { @Handler public void handleAndThrowException(String s) throws Exception { msgHandlerCounter.incrementAndGet(); throw new Exception("error in msg handler on call no. " + msgHandlerCounter.get()); } }); IMessagePublication first = bus.post("first event - event handler will raise exception followed by another exception in the error handler").asynchronously(); IMessagePublication second = bus.post("second event - expecting that msg dispatcher will still dispatch this after encountering exception in error handler").asynchronously(); pause(200); assertEquals("msg handler is called also on the 2nd event after an exception in error handler following 1st event error", 2, msgHandlerCounter.get()); assertFalse("no more messages left to process", bus.hasPendingMessages()); assertTrue(first.hasError()); assertTrue(first.isFinished()); assertNotNull(first.getError().toString()); assertEquals(first.getError().getPublishedMessage(),"first event - event handler will raise exception followed by another exception in the error handler" ); assertTrue(second.hasError()); assertTrue(second.isFinished()); assertNotNull(second.getError().toString()); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/StrongConcurrentSetTest.java000066400000000000000000000026261314636521700270570ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.common.StrongConcurrentSet; import org.junit.Test; import java.util.Collection; import java.util.HashSet; /** * @author bennidi * Date: 3/29/13 */ public class StrongConcurrentSetTest extends ConcurrentSetTest{ protected Collection createSet() { return new StrongConcurrentSet(); } @Test public void testToArray() { Collection set = createSet(); assertFalse(set.contains(1)); set.add(1); set.add(3); set.add(5); Object [] asArray = set.toArray(); // TODO: To array returns set of entries?! } @Test(expected = UnsupportedOperationException.class) public void testContainsAll() { createSet().containsAll(new HashSet()); } @Test(expected = UnsupportedOperationException.class) public void testRemoveAll() { createSet().removeAll(new HashSet()); } @Test(expected = UnsupportedOperationException.class) public void testRetainAll() { createSet().retainAll(new HashSet()); } @Test public void testClear() { Collection set = createSet(); assertFalse(set.contains(1)); set.add(1); assertTrue(set.contains(1)); assertEquals(1, set.size()); set.clear(); assertFalse(set.contains(1)); assertEquals(0, set.size()); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/SubscriptionManagerTest.java000066400000000000000000000315011314636521700270350ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.BusRuntime; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.common.*; import net.engio.mbassy.listener.MetadataReader; import net.engio.mbassy.listeners.*; import net.engio.mbassy.messages.*; import net.engio.mbassy.subscription.Subscription; import net.engio.mbassy.subscription.SubscriptionFactory; import net.engio.mbassy.subscription.SubscriptionManager; import org.junit.Test; import java.util.Collection; import java.util.Collections; /** * Test the subscriptions as generated and organized by the subscription manager. Tests use different sets of listeners * and corresponding expected set of subscriptions that should result from subscribing the listeners. The subscriptions * are tested for the type of messages they should handle and * * @author bennidi * Date: 5/12/13 */ public class SubscriptionManagerTest extends AssertSupport { private static final int InstancesPerListener = 5000; private static final int ConcurrentUnits = 10; @Test public void testIMessageListener() { ListenerFactory listeners = listeners( IMessageListener.DefaultListener.class, IMessageListener.AsyncListener.class, IMessageListener.DisabledListener.class, IMessageListener.NoSubtypesListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(IMessageListener.DefaultListener.class).handles(IMessage.class, AbstractMessage.class, IMultipartMessage.class, StandardMessage.class, MessageTypes.class) .listener(IMessageListener.AsyncListener.class).handles(IMessage.class, AbstractMessage.class, IMultipartMessage.class, StandardMessage.class, MessageTypes.class) .listener(IMessageListener.NoSubtypesListener.class).handles(IMessage.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testAbstractMessageListener() { ListenerFactory listeners = listeners( AbstractMessageListener.DefaultListener.class, AbstractMessageListener.AsyncListener.class, AbstractMessageListener.DisabledListener.class, AbstractMessageListener.NoSubtypesListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(AbstractMessageListener.NoSubtypesListener.class).handles(AbstractMessage.class) .listener(AbstractMessageListener.DefaultListener.class).handles(StandardMessage.class, AbstractMessage.class) .listener(AbstractMessageListener.AsyncListener.class).handles(StandardMessage.class, AbstractMessage.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testMessagesListener() { ListenerFactory listeners = listeners( MessagesTypeListener.DefaultListener.class, MessagesTypeListener.AsyncListener.class, MessagesTypeListener.DisabledListener.class, MessagesTypeListener.NoSubtypesListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(MessagesTypeListener.NoSubtypesListener.class).handles(MessageTypes.class) .listener(MessagesTypeListener.DefaultListener.class).handles(MessageTypes.class) .listener(MessagesTypeListener.AsyncListener.class).handles(MessageTypes.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testMultipartMessageListener() { ListenerFactory listeners = listeners( MultipartMessageListener.DefaultListener.class, MultipartMessageListener.AsyncListener.class, MultipartMessageListener.DisabledListener.class, MultipartMessageListener.NoSubtypesListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(MultipartMessageListener.NoSubtypesListener.class).handles(MultipartMessage.class) .listener(MultipartMessageListener.DefaultListener.class).handles(MultipartMessage.class) .listener(MultipartMessageListener.AsyncListener.class).handles(MultipartMessage.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testIMultipartMessageListener() { ListenerFactory listeners = listeners( IMultipartMessageListener.DefaultListener.class, IMultipartMessageListener.AsyncListener.class, IMultipartMessageListener.DisabledListener.class, IMultipartMessageListener.NoSubtypesListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(IMultipartMessageListener.NoSubtypesListener.class).handles(IMultipartMessage.class) .listener(IMultipartMessageListener.DefaultListener.class).handles(MultipartMessage.class, IMultipartMessage.class) .listener(IMultipartMessageListener.AsyncListener.class).handles(MultipartMessage.class, IMultipartMessage.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testStandardMessageListener() { ListenerFactory listeners = listeners( StandardMessageListener.DefaultListener.class, StandardMessageListener.AsyncListener.class, StandardMessageListener.DisabledListener.class, StandardMessageListener.NoSubtypesListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(StandardMessageListener.NoSubtypesListener.class).handles(StandardMessage.class) .listener(StandardMessageListener.DefaultListener.class).handles(StandardMessage.class) .listener(StandardMessageListener.AsyncListener.class).handles(StandardMessage.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testICountableListener() { ListenerFactory listeners = listeners( ICountableListener.DefaultListener.class, ICountableListener.AsyncListener.class, ICountableListener.DisabledListener.class, ICountableListener.NoSubtypesListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(ICountableListener.DefaultListener.class).handles(ICountable.class) .listener(ICountableListener.DefaultListener.class).handles(MultipartMessage.class, IMultipartMessage.class, ICountable.class, StandardMessage.class) .listener(ICountableListener.AsyncListener.class).handles(MultipartMessage.class, IMultipartMessage.class, ICountable.class, StandardMessage.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testMultipleMessageListeners() { ListenerFactory listeners = listeners( ICountableListener.DefaultListener.class, ICountableListener.AsyncListener.class, ICountableListener.DisabledListener.class, IMultipartMessageListener.DefaultListener.class, IMultipartMessageListener.AsyncListener.class, IMultipartMessageListener.DisabledListener.class, MessagesTypeListener.DefaultListener.class, MessagesTypeListener.AsyncListener.class, MessagesTypeListener.DisabledListener.class); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(ICountableListener.DefaultListener.class) .handles(MultipartMessage.class, IMultipartMessage.class, ICountable.class, StandardMessage.class) .listener(ICountableListener.AsyncListener.class) .handles(MultipartMessage.class, IMultipartMessage.class, ICountable.class, StandardMessage.class) .listener(IMultipartMessageListener.DefaultListener.class).handles(MultipartMessage.class, IMultipartMessage.class) .listener(IMultipartMessageListener.AsyncListener.class).handles(MultipartMessage.class, IMultipartMessage.class) .listener(MessagesTypeListener.DefaultListener.class).handles(MessageTypes.class) .listener(MessagesTypeListener.AsyncListener.class).handles(MessageTypes.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testStrongListenerSubscription() throws Exception { ListenerFactory listeners = listeners(CustomInvocationListener.class); SubscriptionManager subscriptionManager = new SubscriptionManager(new MetadataReader(), new SubscriptionFactory(), mockedRuntime()); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), ConcurrentUnits); listeners.clear(); runGC(); Collection subscriptions = subscriptionManager.getSubscriptionsByMessageType(StandardMessage.class); assertEquals(1, subscriptions.size()); for (Subscription sub : subscriptions) assertEquals(InstancesPerListener, sub.size()); } @Test public void testOverloadedMessageHandlers() { ListenerFactory listeners = listeners( Overloading.ListenerBase.class, Overloading.ListenerSub.class); SubscriptionManager subscriptionManager = new SubscriptionManager(new MetadataReader(), new SubscriptionFactory(), mockedRuntime()); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), ConcurrentUnits); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(Overloading.ListenerBase.class).handles(Overloading.TestMessageA.class, Overloading.TestMessageA.class) .listener(Overloading.ListenerSub.class).handles(Overloading.TestMessageA.class, Overloading.TestMessageA.class, Overloading.TestMessageB.class); runTestWith(listeners, expectedSubscriptions); } @Test public void testPrioritizedMessageHandlers() { ListenerFactory listeners = listeners(PrioritizedListener.class); SubscriptionManager subscriptionManager = new SubscriptionManager(new MetadataReader(), new SubscriptionFactory(), mockedRuntime()); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), ConcurrentUnits); SubscriptionValidator expectedSubscriptions = new SubscriptionValidator(listeners) .listener(PrioritizedListener.class).handles(IMessage.class, IMessage.class, IMessage.class, IMessage.class); runTestWith(listeners, expectedSubscriptions); } private BusRuntime mockedRuntime() { return new BusRuntime(null) .add(IBusConfiguration.Properties.PublicationErrorHandlers, Collections.EMPTY_SET) .add(IBusConfiguration.Properties.AsynchronousHandlerExecutor, null); } private ListenerFactory listeners(Class... listeners) { ListenerFactory factory = new ListenerFactory(); for (Class listener : listeners) { factory.create(InstancesPerListener, listener); } return factory; } private void runTestWith(final ListenerFactory listeners, final SubscriptionValidator validator) { final SubscriptionManager subscriptionManager = new SubscriptionManager(new MetadataReader(), new SubscriptionFactory(), mockedRuntime()); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(subscriptionManager, listeners), ConcurrentUnits); validator.validate(subscriptionManager); ConcurrentExecutor.runConcurrent(TestUtil.unsubscriber(subscriptionManager, listeners), ConcurrentUnits); listeners.clear(); validator.validate(subscriptionManager); } /** * define handlers with different priorities which need to be executed * in their respective order */ public static class PrioritizedListener { @net.engio.mbassy.listener.Handler(priority = 1) public void handlePrio1(IMessage message) { message.handled(this.getClass()); } @net.engio.mbassy.listener.Handler(priority = 2) public void handlePrio2(IMessage message) { message.handled(this.getClass()); } @net.engio.mbassy.listener.Handler(priority = 3) public void handlePrio3(IMessage message) { message.handled(this.getClass()); } @net.engio.mbassy.listener.Handler(priority = 4) public void handlePrio4(IMessage message) { message.handled(this.getClass()); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/SyncAsyncTest.java000066400000000000000000000157061314636521700250010ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.common.*; import net.engio.mbassy.listeners.*; import net.engio.mbassy.messages.MessageTypes; import net.engio.mbassy.messages.MultipartMessage; import net.engio.mbassy.messages.StandardMessage; import org.junit.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Test synchronous and asynchronous dispatch in single and multi-threaded scenario. * * @author bennidi * Date: 2/8/12 */ public class SyncAsyncTest extends MessageBusTest { @Test public void testSyncPublicationSyncHandlers() throws Exception { ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, Listeners.synchronous()) .create(InstancesPerListener, Listeners.noHandlers()); final MBassador bus = createBus(SyncAsync(), listeners); Runnable publishAndCheck = new Runnable() { @Override public void run() { StandardMessage standardMessage = new StandardMessage(); MultipartMessage multipartMessage = new MultipartMessage(); bus.post(standardMessage).now(); bus.post(multipartMessage).now(); bus.post(MessageTypes.Simple).now(); assertEquals(InstancesPerListener, standardMessage.getTimesHandled(IMessageListener.DefaultListener.class)); assertEquals(InstancesPerListener, multipartMessage.getTimesHandled(IMessageListener.DefaultListener.class)); } }; // test single-threaded ConcurrentExecutor.runConcurrent(publishAndCheck, 1); // test multi-threaded MessageTypes.resetAll(); ConcurrentExecutor.runConcurrent(publishAndCheck, ConcurrentUnits); assertEquals(InstancesPerListener * ConcurrentUnits, MessageTypes.Simple.getTimesHandled(IMessageListener.DefaultListener.class)); assertEquals(InstancesPerListener * ConcurrentUnits, MessageTypes.Simple.getTimesHandled(MessagesTypeListener.DefaultListener.class)); bus.shutdown(); pause(200); } @Test public void testSyncPublicationAsyncHandlers() throws Exception { ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, Listeners.asynchronous()) .create(InstancesPerListener, Listeners.noHandlers()); final MBassador bus = createBus(SyncAsync(), listeners); final MessageManager messageManager = new MessageManager(); Runnable publishAndCheck = new Runnable() { @Override public void run() { StandardMessage standardMessage = messageManager.createAndTrack( StandardMessage.class, InstancesPerListener, Listeners.join(Listeners.asynchronous(),Listeners.handlesStandardMessage())); MultipartMessage multipartMessage = messageManager.create( MultipartMessage.class, InstancesPerListener, IMessageListener.AsyncListener.class, IMultipartMessageListener.AsyncListener.class); bus.post(standardMessage).now(); bus.post(multipartMessage).now(); bus.post(MessageTypes.Simple).now(); } }; ConcurrentExecutor.runConcurrent(publishAndCheck, 1); messageManager.waitForMessages(waitForMessageTimeout); MessageTypes.resetAll(); messageManager.register(MessageTypes.Simple, InstancesPerListener * ConcurrentUnits, IMessageListener.AsyncListener.class, MessagesTypeListener.AsyncListener.class); ConcurrentExecutor.runConcurrent(publishAndCheck, ConcurrentUnits); messageManager.waitForMessages(waitForMessageTimeout); bus.shutdown(); pause(200); } @Test public void testAsynchronousMessagePublication() throws Exception { ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, Listeners.asynchronous()) .create(InstancesPerListener, Listeners.noHandlers()); final MBassador bus = createBus(SyncAsync(), listeners); final MessageManager messageManager = new MessageManager(); Runnable publishAndCheck = new Runnable() { @Override public void run() { StandardMessage standardMessage = messageManager.create(StandardMessage.class, InstancesPerListener, IMessageListener.AsyncListener.class); MultipartMessage multipartMessage = messageManager.create(MultipartMessage.class, InstancesPerListener, IMessageListener.AsyncListener.class); bus.post(standardMessage).asynchronously(1, TimeUnit.MILLISECONDS); bus.post(multipartMessage).asynchronously(); bus.post(MessageTypes.Simple).asynchronously(); } }; ConcurrentExecutor.runConcurrent(publishAndCheck, 1); messageManager.waitForMessages(waitForMessageTimeout); MessageTypes.resetAll(); ConcurrentExecutor.runConcurrent(publishAndCheck, ConcurrentUnits); messageManager.waitForMessages(waitForMessageTimeout); bus.shutdown(); pause(200); } @Test public void testExceptionInHandlerInvocation(){ final AtomicInteger exceptionCount = new AtomicInteger(0); IPublicationErrorHandler ExceptionCounter = new IPublicationErrorHandler() { @Override public void handleError(PublicationError error) { exceptionCount.incrementAndGet(); } }; //DS: Exception counter added via config IBusConfiguration config = SyncAsync(false) .addPublicationErrorHandler(ExceptionCounter); final MBassador bus = new MBassador(config); ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, ExceptionThrowingListener.class); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, listeners), ConcurrentUnits); Runnable asynchronousPublication = new Runnable() { @Override public void run() { bus.post(new Object()).asynchronously(); } }; // single threaded ConcurrentExecutor.runConcurrent(asynchronousPublication, 1); pause(processingTimeInMS); assertEquals(InstancesPerListener, exceptionCount.get()); // multi threaded exceptionCount.set(0); ConcurrentExecutor.runConcurrent(asynchronousPublication, ConcurrentUnits); pause(processingTimeInMS); assertEquals(InstancesPerListener * ConcurrentUnits, exceptionCount.get()); bus.shutdown(); pause(200); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/SyncBusTest.java000066400000000000000000000240451314636521700244510ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.SyncMessageBus; import net.engio.mbassy.bus.common.GenericMessagePublicationSupport; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.common.ConcurrentExecutor; import net.engio.mbassy.common.ListenerFactory; import net.engio.mbassy.common.MessageBusTest; import net.engio.mbassy.common.TestUtil; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listeners.CustomInvocationListener; import net.engio.mbassy.listeners.ExceptionThrowingListener; import net.engio.mbassy.listeners.IMessageListener; import net.engio.mbassy.listeners.MessagesTypeListener; import net.engio.mbassy.messages.MessageTypes; import net.engio.mbassy.messages.MultipartMessage; import net.engio.mbassy.messages.StandardMessage; import org.junit.Test; import java.util.concurrent.atomic.AtomicInteger; /** * Test synchronous and asynchronous dispatch in single and multi-threaded scenario. * * @author bennidi * Date: 2/8/12 */ public abstract class SyncBusTest extends MessageBusTest { protected abstract GenericMessagePublicationSupport getSyncMessageBus(boolean failOnException, IPublicationErrorHandler errorHandler); protected abstract GenericMessagePublicationSupport getSyncMessageBus(boolean failOnException); @Test public void testSynchronousMessagePublication() throws Exception { final GenericMessagePublicationSupport bus = getSyncMessageBus(true); ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, IMessageListener.DefaultListener.class) .create(InstancesPerListener, IMessageListener.DisabledListener.class) .create(InstancesPerListener, MessagesTypeListener.DefaultListener.class) .create(InstancesPerListener, MessagesTypeListener.DisabledListener.class) .create(InstancesPerListener, Object.class); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, listeners), ConcurrentUnits); Runnable publishAndCheck = new Runnable() { @Override public void run() { StandardMessage standardMessage = new StandardMessage(); MultipartMessage multipartMessage = new MultipartMessage(); bus.post(standardMessage).now(); bus.post(multipartMessage).now(); bus.post(MessageTypes.Simple).now(); bus.post(MessageTypes.Multipart).now(); assertEquals(InstancesPerListener, standardMessage.getTimesHandled(IMessageListener.DefaultListener.class)); assertEquals(InstancesPerListener, multipartMessage.getTimesHandled(IMessageListener.DefaultListener.class)); } }; // single threaded ConcurrentExecutor.runConcurrent(publishAndCheck, 1); // multi threaded MessageTypes.resetAll(); ConcurrentExecutor.runConcurrent(publishAndCheck, ConcurrentUnits); assertEquals(InstancesPerListener * ConcurrentUnits, MessageTypes.Simple.getTimesHandled(IMessageListener.DefaultListener.class)); assertEquals(InstancesPerListener * ConcurrentUnits, MessageTypes.Multipart.getTimesHandled(IMessageListener.DefaultListener.class)); assertEquals(InstancesPerListener * ConcurrentUnits, MessageTypes.Simple.getTimesHandled(MessagesTypeListener.DefaultListener.class)); assertEquals(InstancesPerListener * ConcurrentUnits, MessageTypes.Multipart.getTimesHandled(MessagesTypeListener.DefaultListener.class)); } @Test public void testExceptionInHandlerInvocation(){ final AtomicInteger exceptionCount = new AtomicInteger(0); IPublicationErrorHandler ExceptionCounter = new IPublicationErrorHandler() { @Override public void handleError(PublicationError error) { exceptionCount.incrementAndGet(); } }; //DS: modified to pass ExceptionCounter via the configuration object final GenericMessagePublicationSupport bus = getSyncMessageBus(false,ExceptionCounter); ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, ExceptionThrowingListener.class); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, listeners), ConcurrentUnits); Runnable publish = new Runnable() { @Override public void run() { bus.post(new Object()).now(); } }; // single threaded ConcurrentExecutor.runConcurrent(publish, 1); assertEquals(InstancesPerListener, exceptionCount.get()); exceptionCount.set(0); // reset for next test // multi threaded ConcurrentExecutor.runConcurrent(publish, ConcurrentUnits); assertEquals(InstancesPerListener * ConcurrentUnits, exceptionCount.get()); } @Test public void testCustomHandlerInvocation(){ final GenericMessagePublicationSupport bus = getSyncMessageBus(true); ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, CustomInvocationListener.class) .create(InstancesPerListener, Object.class); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, listeners), ConcurrentUnits); Runnable publishAndCheck = new Runnable() { @Override public void run() { StandardMessage standardMessage = new StandardMessage(); MultipartMessage multipartMessage = new MultipartMessage(); bus.post(standardMessage).now(); bus.post(multipartMessage).now(); bus.post(MessageTypes.Simple).now(); assertEquals(InstancesPerListener * 2, standardMessage.getTimesHandled(CustomInvocationListener.class)); assertEquals(0, multipartMessage.getTimesHandled(CustomInvocationListener.class)); assertEquals(0, MessageTypes.Simple.getTimesHandled(CustomInvocationListener.class)); } }; // single threaded ConcurrentExecutor.runConcurrent(publishAndCheck, 1); // multi threaded ConcurrentExecutor.runConcurrent(publishAndCheck, ConcurrentUnits); } @Test public void testHandlerPriorities(){ final GenericMessagePublicationSupport bus = getSyncMessageBus(true); ListenerFactory listeners = new ListenerFactory() .create(InstancesPerListener, PrioritizedListener.class) .create(InstancesPerListener, Object.class); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, listeners), ConcurrentUnits); Runnable publishAndCheck = new Runnable() { @Override public void run() { bus.post(new IncrementingMessage()).now(); } }; // single threaded ConcurrentExecutor.runConcurrent(publishAndCheck, 1); // multi threaded ConcurrentExecutor.runConcurrent(publishAndCheck, ConcurrentUnits); } public static class MBassadorTest extends SyncBusTest { //DS: added errorHandler parameter to allow adding handler from caller @Override protected GenericMessagePublicationSupport getSyncMessageBus(boolean failOnException, IPublicationErrorHandler errorHandler) { IBusConfiguration asyncFIFOConfig = new BusConfiguration().addPublicationErrorHandler(new AssertionErrorHandler(failOnException)); asyncFIFOConfig.addFeature(Feature.SyncPubSub.Default()); asyncFIFOConfig.addFeature(Feature.AsynchronousHandlerInvocation.Default(1, 1)); asyncFIFOConfig.addFeature(Feature.AsynchronousMessageDispatch.Default().setNumberOfMessageDispatchers(1)); if (errorHandler != null) { asyncFIFOConfig.addPublicationErrorHandler(errorHandler); } return new MBassador(asyncFIFOConfig); } @Override protected GenericMessagePublicationSupport getSyncMessageBus(boolean failOnException) { return getSyncMessageBus(failOnException, null); } } public static class SyncMessageBusTest extends SyncBusTest { @Override protected GenericMessagePublicationSupport getSyncMessageBus(boolean failOnException, IPublicationErrorHandler errorHandler) { IBusConfiguration syncPubSubCfg = new BusConfiguration().addPublicationErrorHandler(new AssertionErrorHandler(failOnException)); syncPubSubCfg.addFeature(Feature.SyncPubSub.Default()); if (errorHandler != null) { syncPubSubCfg.addPublicationErrorHandler(errorHandler); } return new SyncMessageBus(syncPubSubCfg); } @Override protected GenericMessagePublicationSupport getSyncMessageBus(boolean failOnException) { return getSyncMessageBus(failOnException, null); } } static class IncrementingMessage{ private int count = 1; public void markHandled(int newVal){ // only transitions by the next handler are allowed if(count == newVal || count + 1 == newVal) count = newVal; else throw new RuntimeException("Message was handled out of order"); } } public static class PrioritizedListener{ @Handler(priority = Integer.MIN_VALUE) public void handle1(IncrementingMessage message) { message.markHandled(4); } @Handler(priority = -2) public void handle2(IncrementingMessage message) { message.markHandled(3); } @Handler public void handle3(IncrementingMessage message) { message.markHandled(2); } @Handler(priority = Integer.MAX_VALUE) public void handle4(IncrementingMessage message) { message.markHandled(1); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/SynchronizedHandlerTest.java000066400000000000000000000077201314636521700270410ustar00rootroot00000000000000package net.engio.mbassy; import junit.framework.Assert; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.common.IMessageBus; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.common.MessageBusTest; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Synchronized; import org.junit.Test; import java.util.LinkedList; import java.util.List; /** * * @author bennidi * Date: 3/31/13 */ public class SynchronizedHandlerTest extends MessageBusTest { private static int incrementsPerMessage = 1000; private static int numberOfMessages = 1000; private static int numberOfListeners = 1000; @Test public void testSynchronizedWithSynchronousInvocation(){ List handlers = new LinkedList(); IBusConfiguration config = SyncAsync(true); config.getFeature(Feature.AsynchronousMessageDispatch.class) .setNumberOfMessageDispatchers(6); IMessageBus bus = createBus(config); for(int i = 0; i < numberOfListeners; i++){ SynchronizedWithSynchronousDelivery handler = new SynchronizedWithSynchronousDelivery(); handlers.add(handler); bus.subscribe(handler); } IMessagePublication publication = null; for(int i = 0; i < numberOfMessages; i++){ publication = bus.post(new Object()).asynchronously(); } // wait for last publication while (!publication.isFinished()){ pause(100); } for(SynchronizedWithSynchronousDelivery handler : handlers){ assertEquals(incrementsPerMessage * numberOfMessages, handler.counter); } } @Test public void testSynchronizedWithAsSynchronousInvocation(){ List handlers = new LinkedList(); IBusConfiguration config = SyncAsync(true); config.getFeature(Feature.AsynchronousMessageDispatch.class) .setNumberOfMessageDispatchers(6); IMessageBus bus = createBus(config); for(int i = 0; i < numberOfListeners; i++){ SynchronizedWithAsynchronousDelivery handler = new SynchronizedWithAsynchronousDelivery(); handlers.add(handler); bus.subscribe(handler); } for(int i = 0; i < numberOfMessages; i++){ track(bus.post(new Object()).asynchronously()); } // Check the handlers processing status // Define timeframe in which processing should be finished // If not then an error is assumed long timeElapsed = 0; long timeOut = 30000; // 30 seconds long begin = System.currentTimeMillis(); while (timeElapsed < timeOut) { boolean successful = true; for (SynchronizedWithAsynchronousDelivery handler : handlers) { successful &= incrementsPerMessage * numberOfMessages == handler.counter; } if(successful) break; timeElapsed = System.currentTimeMillis() - begin; } if(timeElapsed >= timeOut) Assert.fail("Processing of handlers unfinished after timeout"); } public static class SynchronizedWithSynchronousDelivery { private int counter = 0; @Handler @Synchronized public void handleMessage(Object o){ for(int i = 0; i < incrementsPerMessage; i++){ counter++; } } } public static class SynchronizedWithAsynchronousDelivery { private int counter = 0; @Handler(delivery = Invoke.Asynchronously) @Synchronized public void handleMessage(Object o){ for(int i = 0; i < incrementsPerMessage; i++){ counter++; } } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/WeakConcurrentSetTest.java000066400000000000000000000037561314636521700264770ustar00rootroot00000000000000package net.engio.mbassy; import net.engio.mbassy.common.ConcurrentExecutor; import net.engio.mbassy.common.WeakConcurrentSet; import org.junit.Before; import org.junit.Test; import java.util.Collection; import java.util.HashSet; import java.util.Random; import java.util.Set; /** * * * @author bennidi * Date: 3/29/13 */ public class WeakConcurrentSetTest extends ConcurrentSetTest{ @Override protected Collection createSet() { return new WeakConcurrentSet(); } @Test public void testIteratorCleanup() { // Assemble final HashSet permanentElements = new HashSet(); final Collection testSetWeak = createSet(); final Random rand = new Random(); for (int i = 0; i < numberOfElements; i++) { Object candidate = new Object(); if (rand.nextInt() % 3 == 0) { permanentElements.add(candidate); } testSetWeak.add(candidate); } // Remove/Garbage collect all objects that have not // been inserted into the set of permanent candidates. runGC(); ConcurrentExecutor.runConcurrent(new Runnable() { @Override public void run() { for (Object testObject : testSetWeak) { // do nothing // just iterate to trigger automatic clean up System.currentTimeMillis(); } } }, numberOfThreads); // the set should have cleaned up the garbage collected elements // it must still contain all of the permanent objects // since different GC mechanisms can be used (not necessarily full, stop-the-world) not all dead objects // must have been collected assertTrue(permanentElements.size() <= testSetWeak.size() && testSetWeak.size() < numberOfElements); for (Object test : testSetWeak) { assertTrue(permanentElements.contains(test)); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/bus/000077500000000000000000000000001314636521700221445ustar00rootroot00000000000000mbassador-1.3.1/src/test/java/net/engio/mbassy/bus/AbstractPubSubSupportTest.java000066400000000000000000000145461314636521700301420ustar00rootroot00000000000000package net.engio.mbassy.bus; import junit.framework.Assert; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.common.MessageBusTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.concurrent.atomic.AtomicInteger; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Added for changes proposed under https://github.com/bennidi/mbassador/issues/106 *

* Created by David Sowerby on 13/04/15. */ @RunWith(MockitoJUnitRunner.class) public class AbstractPubSubSupportTest { IBusConfiguration configuration; @Mock IPublicationErrorHandler handler1; @Mock IPublicationErrorHandler handler2; @Mock IPublicationErrorHandler handler3; @Mock PublicationError publicationError; @Before public void setup() { configuration = MessageBusTest.SyncAsync(false); } @Test public void testHandlePublicationError_handlers_present_sync() { //given configuration.addPublicationErrorHandler(handler1); configuration.addPublicationErrorHandler(handler2); configuration.addPublicationErrorHandler(handler3); //when SyncMessageBus bus = new SyncMessageBus(configuration); bus.handlePublicationError(publicationError); //then verify(handler1).handleError(publicationError); verify(handler2).handleError(publicationError); verify(handler3).handleError(publicationError); } @Test public void testHandlePublicationError_handlers_present_async() { //given configuration.addPublicationErrorHandler(handler1); configuration.addPublicationErrorHandler(handler2); configuration.addPublicationErrorHandler(handler3); //when MBassador bus = new MBassador(configuration); bus.handlePublicationError(publicationError); //then verify(handler1).handleError(publicationError); verify(handler2).handleError(publicationError); verify(handler3).handleError(publicationError); } @Test public void testHandlePublicationError_construct_with_handler_sync() { //given //when SyncMessageBus bus = new SyncMessageBus(handler1); bus.handlePublicationError(publicationError); //then verify(handler1).handleError(publicationError); } @Test public void testHandlePublicationError_constrcut_with_handler_async() { //given configuration.addPublicationErrorHandler(handler1); //when MBassador bus = new MBassador(handler1); bus.handlePublicationError(publicationError); //then verify(handler1).handleError(publicationError); } /** * Test configuration that does not provide a publication error handler. * This should print a warning message and fallback to STDOUT handler */ @Test public void testHandlePublicationError_no_handlers_present_construct_with_config_async() { //given PrintStream old = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); old = System.out; System.setOut(ps); //when no publication error handler is provided MBassador bus = new MBassador(configuration); // then we see the warning on the console Assert.assertTrue(baos.toString().contains(AbstractPubSubSupport.ERROR_HANDLER_MSG)); } finally { System.out.flush(); if (old != null) { System.setOut(old); } } } @Test public void testHandlePublicationError_default_construct_sync() { //given final String errorMsg = "Test error"; when(publicationError.toString()).thenReturn(errorMsg); PrintStream old = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); old = System.out; System.setOut(ps); //when SyncMessageBus bus = new SyncMessageBus(); Assert.assertTrue(baos.toString().contains(AbstractPubSubSupport.ERROR_HANDLER_MSG)); bus.handlePublicationError(publicationError); //then Assert.assertTrue(baos.toString().contains(errorMsg)); } finally { System.out.flush(); if (old != null) { System.setOut(old); } } } @Test public void testHandlePublicationError_default_construct_async() { //given final String errorMsg = "Test error"; when(publicationError.toString()).thenReturn(errorMsg); PrintStream old = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); old = System.out; System.setOut(ps); //when MBassador bus = new MBassador(); Assert.assertTrue(baos.toString().contains(AbstractPubSubSupport.ERROR_HANDLER_MSG)); bus.handlePublicationError(publicationError); //then Assert.assertTrue(baos.toString().contains(errorMsg)); } finally { System.out.flush(); if (old != null) { System.setOut(old); } } } @Test public void testHandlePublicationError_raises_exception() { final AtomicInteger invocationCounter = new AtomicInteger(0); SyncMessageBus bus = new SyncMessageBus(new IPublicationErrorHandler() { @Override public void handleError(PublicationError error) { invocationCounter.incrementAndGet(); throw new RuntimeException("exception encountered in error handler"); } }); bus.handlePublicationError(publicationError); Assert.assertEquals(1, invocationCounter.get()); } }mbassador-1.3.1/src/test/java/net/engio/mbassy/common/000077500000000000000000000000001314636521700226435ustar00rootroot00000000000000mbassador-1.3.1/src/test/java/net/engio/mbassy/common/AssertSupport.java000066400000000000000000000046061314636521700263520ustar00rootroot00000000000000package net.engio.mbassy.common; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.ref.WeakReference; /** * @author bennidi */ public class AssertSupport { private Runtime runtime = Runtime.getRuntime(); protected Logger logger = LoggerFactory.getLogger(getClass().getSimpleName()); private volatile long testExecutionStart; @Rule public TestName name = new TestName(); @Before public void beforeTest(){ logger.info("Running test " + getTestName()); testExecutionStart = System.currentTimeMillis(); } @After public void afterTest(){ logger.info(String.format("Finished " + getTestName() + ": " + (System.currentTimeMillis() - testExecutionStart) + " ms")); } public void pause(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { e.printStackTrace(); } } public void pause() { pause(10); } public String getTestName(){ return getClass().getSimpleName() + "." + name.getMethodName(); } public void runGC() { WeakReference ref = new WeakReference(new Object()); pause(100); while(ref.get() != null) { pause(10); runtime.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(String message, Object expected, Object actual) { Assert.assertEquals(message, expected, actual); } public void assertEquals(Object expected, Object actual) { Assert.assertEquals(expected, actual); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/common/ConcurrentExecutor.java000066400000000000000000000044021314636521700273470ustar00rootroot00000000000000package net.engio.mbassy.common; import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; /** * Run various tests concurrently. A given instance of runnable will be used to spawn and start * as many threads as specified by an additional parameter or (if multiple runnables have been * passed to the method) one thread for each runnable. *

* Date: 2/14/12 * * @author bennidi */ public class ConcurrentExecutor { public static void runConcurrent(final Runnable unit, int numberOfConcurrentExecutions) { Runnable[] units = new Runnable[numberOfConcurrentExecutions]; // create the tasks and schedule for execution for (int i = 0; i < numberOfConcurrentExecutions; i++) { units[i] = unit; } runConcurrent(units); } public static void runConcurrent(int numberOfConcurrentExecutions, final Runnable... units) { Runnable[] runnables = new Runnable[numberOfConcurrentExecutions * units.length]; // create the tasks and schedule for execution for (int i = 0; i < numberOfConcurrentExecutions; i++) { for(int k = 0; k < units.length; k++) runnables[k * numberOfConcurrentExecutions +i] = units[k]; } runConcurrent(runnables); } public static void runConcurrent(final Runnable... units) { ExecutorService executor = Executors.newCachedThreadPool(); List> returnValues = new ArrayList>(); // create the tasks and schedule for execution for (final Runnable unit : units) { Callable wrapper = new Callable() { @Override public Long call() throws Exception { long start = System.currentTimeMillis(); unit.run(); return System.currentTimeMillis() - start; } }; returnValues.add(executor.submit(wrapper)); } // wait until all tasks have been executed try { executor.shutdown();// tells the thread pool to execute all waiting tasks executor.awaitTermination(5, TimeUnit.MINUTES); } catch (InterruptedException e) { // unlikely that this will happen e.printStackTrace(); } for(Future task : returnValues){ try { task.get(); } catch (Exception e) { throw new RuntimeException(e); } } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/common/ListenerFactory.java000066400000000000000000000065671314636521700266410ustar00rootroot00000000000000package net.engio.mbassy.common; import junit.framework.Assert; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** * The factory can be used to declaratively specify how many instances of some given classes * should be created. It will create those instances using reflection and provide a list containing those instances. * The factory also holds strong references to the instances such that GC will not interfere with tests unless the * factory is explicitly cleared. * * @author bennidi * Date: 11/22/12 */ public class ListenerFactory { private Map requiredBeans = new HashMap(); private volatile List generatedListeners; private int requiredSize = 0; public int getNumberOfListeners(Class listener){ return requiredBeans.containsKey(listener) ? requiredBeans.get(listener) : 0; } public ListenerFactory create(int numberOfInstances, Class clazz){ requiredBeans.put(clazz, numberOfInstances); requiredSize +=numberOfInstances; return this; } public ListenerFactory create(int numberOfInstances, Class ...classes){ for(Class clazz : classes) create(numberOfInstances,clazz); return this; } public ListenerFactory create(int numberOfInstances, Collection classes){ for(Class clazz : classes) create(numberOfInstances,clazz); return this; } public synchronized List getAll(){ if(generatedListeners != null) return generatedListeners; List listeners = new ArrayList(requiredSize); try { for(Class clazz : requiredBeans.keySet()){ int numberOfRequiredBeans = requiredBeans.get(clazz); for(int i = 0; i < numberOfRequiredBeans; i++){ listeners.add(clazz.newInstance()); } } } catch (Exception e) { // if instantiation fails, counts will be incorrect // -> fail early here Assert.fail("There was a problem instantiating a listener " + e); } Collections.shuffle(listeners); generatedListeners = Collections.unmodifiableList(listeners); return generatedListeners; } // not thread-safe but not yet used concurrently public synchronized void clear(){ generatedListeners = null; requiredBeans.clear(); } /** * Create a thread-safe read-only iterator * * NOTE: Iterator is not perfectly synchronized with mutator methods of the list of generated listeners * In theory, it is possible that the list is changed while iterators are still running which should be avoided. */ public Iterator iterator(){ getAll(); final AtomicInteger current = new AtomicInteger(0); return new Iterator() { @Override public boolean hasNext() { return current.get() < generatedListeners.size(); } @Override public Object next() { int index = current.getAndIncrement(); return index < generatedListeners.size() ? generatedListeners.get(index) : null; } @Override public void remove() { throw new UnsupportedOperationException("Iterator is read only"); } }; } } mbassador-1.3.1/src/test/java/net/engio/mbassy/common/MessageBusTest.java000066400000000000000000000074771314636521700264230ustar00rootroot00000000000000package net.engio.mbassy.common; import net.engio.mbassy.bus.IMessagePublication; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.config.BusConfiguration; import net.engio.mbassy.bus.config.Feature; import net.engio.mbassy.bus.config.IBusConfiguration; import net.engio.mbassy.bus.error.IPublicationErrorHandler; import net.engio.mbassy.bus.error.PublicationError; import net.engio.mbassy.messages.MessageTypes; import org.junit.Before; /** * A base test that provides a factory for message bus that makes tests fail if any * publication error occurs * * @author bennidi * Date: 3/2/13 */ public abstract class MessageBusTest extends AssertSupport { // this value probably needs to be adjusted depending on the performance of the underlying platform // otherwise the tests will fail since asynchronous processing might not have finished when // evaluation is run protected static final int waitForMessageTimeout = 60000; protected static final int processingTimeInMS = 2000; protected static final int InstancesPerListener = 5000; protected static final int ConcurrentUnits = 10; protected static final int IterationsPerThread = 100; public static final class AssertionErrorHandler implements IPublicationErrorHandler{ private boolean failOnException; public AssertionErrorHandler(boolean failOnException) { this.failOnException = failOnException; } @Override public void handleError(PublicationError error) { if(failOnException) org.junit.Assert.fail(error.getCause().getMessage()); } } public static final class EmptyErrorHandler implements IPublicationErrorHandler{ @Override public void handleError(PublicationError error) { } } private StrongConcurrentSet issuedPublications = new StrongConcurrentSet(); @Before public void setUp(){ issuedPublications = new StrongConcurrentSet(); for(MessageTypes mes : MessageTypes.values()) mes.reset(); } public static IBusConfiguration SyncAsync() { return SyncAsync(true); } public static IBusConfiguration SyncAsync(boolean failOnError) { IBusConfiguration config = new BusConfiguration() .addFeature(Feature.SyncPubSub.Default()) .addFeature(Feature.AsynchronousHandlerInvocation.Default()) .addFeature(Feature.AsynchronousMessageDispatch.Default()); if(failOnError) config.addPublicationErrorHandler(new AssertionErrorHandler(failOnError)); return config; } public MBassador createBus(IBusConfiguration configuration) { MBassador bus = new MBassador(configuration); return bus; } public MBassador createBus(IBusConfiguration configuration, ListenerFactory listeners) { MBassador bus = new MBassador(configuration); ConcurrentExecutor.runConcurrent(TestUtil.subscriber(bus, listeners), ConcurrentUnits); return bus; } protected void track(IMessagePublication asynchronously) { issuedPublications.add(asynchronously); } public void waitForPublications(long timeOutInMs){ long start = System.currentTimeMillis(); while(issuedPublications.size() > 0 && System.currentTimeMillis() - start < timeOutInMs){ for(IMessagePublication pub : issuedPublications){ if(pub.isFinished()) issuedPublications.remove(pub); } } if(issuedPublications.size() > 0) fail("Issued publications did not finish within specified timeout of " + timeOutInMs + " ms"); } public void addPublication(IMessagePublication publication){ issuedPublications.add(publication); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/common/MessageManager.java000066400000000000000000000115061314636521700263700ustar00rootroot00000000000000package net.engio.mbassy.common; import net.engio.mbassy.messages.IMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; /** * */ public class MessageManager { private static final Logger LOG = LoggerFactory.getLogger(MessageManager.class); private StrongConcurrentSet messages = new StrongConcurrentSet(); public T create(Class messageType, int expectedCount, Class ...listeners){ T message; try { message = messageType.newInstance(); register(message, expectedCount, listeners); } catch (Exception e) { throw new RuntimeException(e); } return message; } public T createAndTrack(Class messageType, int expectedCount, Collection listeners){ T message; try { message = messageType.newInstance(); register(message, expectedCount, listeners); } catch (Exception e) { throw new RuntimeException(e); } return message; } public void register(T message, int expectedCount, Class ...listeners){ try { messages.add(new MessageContext(expectedCount, message, listeners)); } catch (Exception e) { throw new RuntimeException(e); } } public void register(T message, int expectedCount, Collection listeners){ try { messages.add(new MessageContext(expectedCount, message, listeners)); } catch (Exception e) { throw new RuntimeException(e); } } public void waitForMessages(int timeoutInMs){ long start = System.currentTimeMillis(); while(System.currentTimeMillis() - start < timeoutInMs && messages.size() > 0){ // check each created message once for(MessageContext messageCtx : messages){ boolean handledCompletely = true; for(Class listener : messageCtx.getListeners()){ handledCompletely &= messageCtx.getMessage().getTimesHandled(listener) == messageCtx.getExpectedCount(); } // remove the ones that were handled as expected if(handledCompletely){ logSuccess(messageCtx); messages.remove(messageCtx); } } pause(100); } if(messages.size() > 0){ logFailingMessages(messages); org.junit.Assert.fail("Message were not fully processed in given time"); } } private void pause(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { e.printStackTrace(); } } private void logSuccess(MessageContext mCtx){ LOG.info(mCtx.getMessage().getClass().getSimpleName() + " successfully handled " + mCtx.getExpectedCount() + " times by " + mCtx.printListeners()); } private void logFailingMessages(StrongConcurrentSet failing){ StringBuilder errorMessage = new StringBuilder(); errorMessage.append("Failing messages:\n"); for(MessageContext failingMessage : failing) errorMessage.append(failingMessage); LOG.info(errorMessage.toString()); } private class MessageContext{ private long expectedCount; private IMessage message; private Class[] listeners; private MessageContext(long expectedCount, IMessage message, Class[] listeners) { this.expectedCount = expectedCount; this.message = message; this.listeners = listeners; } private MessageContext(long expectedCount, IMessage message, Collection listeners) { this.expectedCount = expectedCount; this.message = message; this.listeners = listeners.toArray(new Class[]{}); } private long getExpectedCount() { return expectedCount; } private IMessage getMessage() { return message; } private Class[] getListeners() { return listeners; } private String printListeners(){ StringBuilder listenersAsString = new StringBuilder(); for(Class listener : listeners){ listenersAsString.append(listener.getName()); listenersAsString.append(","); } return listenersAsString.toString(); } @Override public String toString() { // TODO: actual count of listeners return message.getClass().getSimpleName() + "{" + "expectedCount=" + expectedCount + ", listeners=" + printListeners() + '}'; } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/common/SubscriptionValidator.java000066400000000000000000000076011314636521700300440ustar00rootroot00000000000000package net.engio.mbassy.common; import net.engio.mbassy.subscription.Subscription; import net.engio.mbassy.subscription.SubscriptionManager; import java.util.*; /** * * @author bennidi * Date: 5/25/13 */ public class SubscriptionValidator extends AssertSupport{ private List validations = new LinkedList(); private Set messageTypes = new HashSet(); private ListenerFactory subscribedListener; // the subscribed listeners are used to assert the size of the subscriptions public SubscriptionValidator(ListenerFactory subscribedListener) { this.subscribedListener = subscribedListener; } public Expectation listener(Class subscriber){ return new Expectation(subscriber); } private SubscriptionValidator expect(Class subscriber, Class messageType){ validations.add(new ValidationEntry(messageType, subscriber)); messageTypes.add(messageType); return this; } // match subscriptions with existing validation entries // for each tuple of subscriber and message type the specified number of listeners must exist public void validate(SubscriptionManager manager){ for(Class messageType : messageTypes){ Collection subscriptions = manager.getSubscriptionsByMessageType(messageType); ensureOrdering(subscriptions); Collection validationEntries = getEntries(EntriesByMessageType(messageType)); assertEquals(subscriptions.size(), validationEntries.size()); for(ValidationEntry validationValidationEntry : validationEntries){ Subscription matchingSub = null; // one of the subscriptions must belong to the subscriber type for(Subscription sub : subscriptions){ if(sub.belongsTo(validationValidationEntry.subscriber)){ matchingSub = sub; break; } } assertNotNull(matchingSub); assertEquals(subscribedListener.getNumberOfListeners(validationValidationEntry.subscriber), matchingSub.size()); } } } private void ensureOrdering(Collection subscriptions){ int lastPriority = Integer.MAX_VALUE;// highest priority possible for(Subscription sub : subscriptions){ assertTrue("Subscriptions should be ordered by priority (DESC)", lastPriority >= sub.getPriority()); lastPriority = sub.getPriority(); } } private Collection getEntries(IPredicate filter){ Collection matching = new LinkedList(); for (ValidationEntry validationValidationEntry : validations){ if(filter.apply(validationValidationEntry))matching.add(validationValidationEntry); } return matching; } private IPredicate EntriesByMessageType(final Class messageType){ return new IPredicate() { @Override public boolean apply(ValidationEntry target) { return target.messageType.equals(messageType); } }; } public class Expectation{ private Class listener; private Expectation(Class listener) { this.listener = listener; } public SubscriptionValidator handles(Class ...messages){ for(Class message : messages) expect(listener, message); return SubscriptionValidator.this; } } private class ValidationEntry { private Class subscriber; private Class messageType; private ValidationEntry(Class messageType, Class subscriber) { this.messageType = messageType; this.subscriber = subscriber; } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/common/TestUtil.java000066400000000000000000000063521314636521700252710ustar00rootroot00000000000000package net.engio.mbassy.common; import net.engio.mbassy.bus.MBassador; import net.engio.mbassy.bus.common.PubSubSupport; import net.engio.mbassy.subscription.SubscriptionManager; import java.util.Iterator; import java.util.List; /** * Todo: Add javadoc * * @author bennidi * Date: 11/22/12 */ public class TestUtil { public static Runnable subscriber(final SubscriptionManager manager, final ListenerFactory listeners){ final Iterator source = listeners.iterator(); return new Runnable() { @Override public void run() { Object next; while((next = source.next()) != null){ manager.subscribe(next); } } }; } public static Runnable unsubscriber(final SubscriptionManager manager, final ListenerFactory listeners){ final Iterator source = listeners.iterator(); return new Runnable() { @Override public void run() { Object next; while((next = source.next()) != null){ manager.unsubscribe(next); } } }; } public static Runnable subscriber(final PubSubSupport bus, final ListenerFactory listeners){ final Iterator source = listeners.iterator(); return new Runnable() { @Override public void run() { Object next; while((next = source.next()) != null){ bus.subscribe(next); } } }; } public static Runnable unsubscriber(final PubSubSupport bus, final ListenerFactory listeners){ final Iterator source = listeners.iterator(); return new Runnable() { @Override public void run() { Object next; while((next = source.next()) != null){ bus.unsubscribe(next); } } }; } public static void setup(final PubSubSupport bus, final List 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 listenerSubset = listeners.subList(partitionStart, partitionEnd); public void run() { for(Object listener : listenerSubset){ bus.subscribe(listener); } } }; } ConcurrentExecutor.runConcurrent(setupUnits); } public static void setup(MBassador bus, ListenerFactory listeners, int numberOfThreads) { setup(bus, listeners.getAll(), numberOfThreads); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/000077500000000000000000000000001314636521700233635ustar00rootroot00000000000000mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/AbstractMessageListener.java000066400000000000000000000026271314636521700310130ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.AbstractMessage; /** * * @author bennidi * Date: 5/24/13 */ @Listener(references = References.Weak) public class AbstractMessageListener { private static abstract class BaseListener { @Handler public void handle(AbstractMessage message){ message.handled(this.getClass()); } } public static class DefaultListener extends BaseListener { public void handle(AbstractMessage message){ super.handle(message); } } public static class NoSubtypesListener extends BaseListener { @Handler(rejectSubtypes = true, priority = 4) public void handle(AbstractMessage message){ super.handle(message); } } public static class AsyncListener extends BaseListener { @Handler(delivery = Invoke.Asynchronously, priority = Integer.MAX_VALUE) public void handle(AbstractMessage message){ super.handle(message); } } public static class DisabledListener extends BaseListener { @Handler(enabled = false) public void handle(AbstractMessage message){ super.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/CustomInvocationListener.java000066400000000000000000000021751314636521700312450ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.bus.MessagePublication; import net.engio.mbassy.dispatch.HandlerInvocation; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.StandardMessage; import net.engio.mbassy.subscription.SubscriptionContext; /** * @author bennidi * Date: 5/25/13 */ @Listener(references = References.Strong) public class CustomInvocationListener { @Handler(invocation = HandleSubTestEventInvocation.class) public void handle(StandardMessage message) { message.handled(this.getClass()); message.handled(this.getClass()); } public static class HandleSubTestEventInvocation extends HandlerInvocation { public HandleSubTestEventInvocation(SubscriptionContext context) { super(context); } @Override public void invoke(CustomInvocationListener listener, StandardMessage message, MessagePublication publication) { listener.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/ExceptionThrowingListener.java000066400000000000000000000006701314636521700314170ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; /** * @author bennidi * Date: 5/25/13 */ @Listener(references = References.Strong) public class ExceptionThrowingListener { @Handler() public void handle(Object message) { throw new RuntimeException("This is an expected exception"); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/ICountableListener.java000066400000000000000000000025141314636521700277630ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.ICountable; /** * * @author bennidi * Date: 5/24/13 */ public class ICountableListener { @Listener(references = References.Weak) private static abstract class BaseListener { @Handler public void handle(ICountable message){ message.handled(this.getClass()); } } public static class DefaultListener extends BaseListener { public void handle(ICountable message){ super.handle(message); } } public static class NoSubtypesListener extends BaseListener { @Handler(rejectSubtypes = true) public void handle(ICountable message){ super.handle(message); } } public static class AsyncListener extends BaseListener { @Handler(delivery = Invoke.Asynchronously) public void handle(ICountable message){ super.handle(message); } } public static class DisabledListener extends BaseListener { @Handler(enabled = false) public void handle(ICountable message){ super.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/IMessageListener.java000066400000000000000000000026501314636521700274340ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.IMessage; /** * A collection of different listener flavours meant for convenient reuse in different * test scenarios. * * @author bennidi * Date: 5/24/13 */ public class IMessageListener { @Listener(references = References.Weak) private static abstract class BaseListener { @Handler public void handle(IMessage message){ message.handled(this.getClass()); } } public static class DefaultListener extends BaseListener { public void handle(IMessage message){ super.handle(message); } } public static class NoSubtypesListener extends BaseListener { @Handler(rejectSubtypes = true) public void handle(IMessage message){ super.handle(message); } } public static class AsyncListener extends BaseListener { @Handler(delivery = Invoke.Asynchronously) public void handle(IMessage message){ super.handle(message); } } public static class DisabledListener extends BaseListener { @Handler(enabled = false) public void handle(IMessage message){ super.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/IMultipartMessageListener.java000066400000000000000000000027101314636521700313330ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.IMultipartMessage; /** * * @author bennidi * Date: 5/24/13 */ public class IMultipartMessageListener { @Listener(references = References.Weak) private static abstract class BaseListener { @Handler public void handle(IMultipartMessage message){ message.handled(this.getClass()); } } public static class DefaultListener extends BaseListener { public void handle(IMultipartMessage message){ super.handle(message); } } public static class NoSubtypesListener extends BaseListener { @Handler(rejectSubtypes = true, priority = Integer.MIN_VALUE) public void handle(IMultipartMessage message){ super.handle(message); } } public static class AsyncListener extends BaseListener { @Handler(delivery = Invoke.Asynchronously, priority = Integer.MIN_VALUE) public void handle(IMultipartMessage message){ super.handle(message); } } public static class DisabledListener extends BaseListener { @Handler(enabled = false , priority = 4) public void handle(IMultipartMessage message){ super.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/Listeners.java000066400000000000000000000105401314636521700261760ustar00rootroot00000000000000package net.engio.mbassy.listeners; import java.util.*; /** * Convenience class to create sets of listeners that can be reused on different test scenarios. */ public class Listeners { private static final List Synchronous = Collections.unmodifiableList(Arrays.asList(new Class[]{ MessagesTypeListener.DefaultListener.class, IMessageListener.DefaultListener.class, StandardMessageListener.DefaultListener.class, MultipartMessageListener.DefaultListener.class, ICountableListener.DefaultListener.class, IMultipartMessageListener.DefaultListener.class})); private static final List Asynchronous = Collections.unmodifiableList(Arrays.asList(new Class[]{ MessagesTypeListener.AsyncListener.class, IMessageListener.AsyncListener.class, StandardMessageListener.AsyncListener.class, MultipartMessageListener.AsyncListener.class, ICountableListener.AsyncListener.class, IMultipartMessageListener.AsyncListener.class})); private static final List SubtypeRejecting = Collections.unmodifiableList(Arrays.asList(new Class[]{ MessagesTypeListener.NoSubtypesListener.class, IMessageListener.NoSubtypesListener.class, StandardMessageListener.NoSubtypesListener.class, MultipartMessageListener.NoSubtypesListener.class, ICountableListener.NoSubtypesListener.class, IMultipartMessageListener.NoSubtypesListener.class})); private static final List NoHandlers = Collections.unmodifiableList(Arrays.asList(new Class[]{ MessagesTypeListener.DisabledListener.class, IMessageListener.DisabledListener.class, StandardMessageListener.DisabledListener.class, MultipartMessageListener.DisabledListener.class, ICountableListener.DisabledListener.class, IMultipartMessageListener.DisabledListener.class, Object.class,String.class})); private static final List HandlesIMessage = Collections.unmodifiableList(Arrays.asList(new Class[]{ IMessageListener.DefaultListener.class, IMessageListener.AsyncListener.class, IMessageListener.NoSubtypesListener.class, IMultipartMessageListener.DefaultListener.class, IMultipartMessageListener.AsyncListener.class, IMultipartMessageListener.NoSubtypesListener.class, MessagesTypeListener.DefaultListener.class, MessagesTypeListener.AsyncListener.class, MessagesTypeListener.NoSubtypesListener.class, StandardMessageListener.DefaultListener.class, StandardMessageListener.AsyncListener.class, StandardMessageListener.NoSubtypesListener.class, MultipartMessageListener.DefaultListener.class, MultipartMessageListener.AsyncListener.class, MultipartMessageListener.NoSubtypesListener.class})); private static final List HandlesStandardessage = Collections.unmodifiableList(Arrays.asList(new Class[]{ IMessageListener.DefaultListener.class, IMessageListener.AsyncListener.class, ICountableListener.DefaultListener.class, ICountableListener.AsyncListener.class, StandardMessageListener.DefaultListener.class, StandardMessageListener.AsyncListener.class, StandardMessageListener.NoSubtypesListener.class})); public static Collection synchronous(){ return Synchronous; } public static Collection asynchronous(){ return Asynchronous; } public static Collection subtypeRejecting(){ return SubtypeRejecting; } public static Collection noHandlers(){ return NoHandlers; } public static Collection handlesIMessage(){ return HandlesIMessage; } public static Collection handlesStandardMessage(){ return HandlesStandardessage; } public static Collection join(Collection...listenerSets){ Set join = new HashSet(); for(Collection listeners : listenerSets) join.addAll(listeners); for(Collection listeners : listenerSets) join.retainAll(listeners); return join; } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/MessagesTypeListener.java000066400000000000000000000025321314636521700303470ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.MessageTypes; /** * * @author bennidi * Date: 5/24/13 */ public class MessagesTypeListener { @Listener(references = References.Weak) private static abstract class BaseListener { @Handler public void handle(MessageTypes message){ message.handled(this.getClass()); } } public static class DefaultListener extends BaseListener { public void handle(MessageTypes message){ super.handle(message); } } public static class NoSubtypesListener extends BaseListener { @Handler(rejectSubtypes = true) public void handle(MessageTypes message){ super.handle(message); } } public static class AsyncListener extends BaseListener { @Handler(delivery = Invoke.Asynchronously) public void handle(MessageTypes message){ super.handle(message); } } public static class DisabledListener extends BaseListener { @Handler(enabled = false) public void handle(MessageTypes message){ super.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/MultipartMessageListener.java000066400000000000000000000025661314636521700312330ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.MultipartMessage; /** * * @author bennidi * Date: 5/24/13 */ public class MultipartMessageListener { @Listener(references = References.Weak) private static abstract class BaseListener { @Handler public void handle(MultipartMessage message){ message.handled(this.getClass()); } } public static class DefaultListener extends BaseListener { public void handle(MultipartMessage message){ super.handle(message); } } public static class NoSubtypesListener extends BaseListener { @Handler(rejectSubtypes = true) public void handle(MultipartMessage message){ super.handle(message); } } public static class AsyncListener extends BaseListener { @Handler(delivery = Invoke.Asynchronously) public void handle(MultipartMessage message){ super.handle(message); } } public static class DisabledListener extends BaseListener { @Handler(enabled = false) public void handle(MultipartMessage message){ super.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/ObjectListener.java000066400000000000000000000010301314636521700271340ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import java.util.Collections; import java.util.LinkedList; import java.util.List; @Listener(references = References.Weak) public class ObjectListener { private List handledMessages = Collections.synchronizedList(new LinkedList()); @Handler(priority = Integer.MAX_VALUE) public void handle(Object message){ handledMessages.add(message); } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/Overloading.java000066400000000000000000000021771314636521700265060ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.AbstractMessage; /** * Some handlers and message types to test correct functioning of overloaded * message handlers * */ public class Overloading { public static class TestMessageA extends AbstractMessage {} public static class TestMessageB extends AbstractMessage {} public static class ListenerSub extends ListenerBase { @Handler public void handleEvent(TestMessageB event) { event.handled(this.getClass()); } } @Listener(references = References.Strong) public static class ListenerBase { /** * (!) If this method is removed, NO event handler will be called. */ @Handler public void handleEventWithNonOverloadedMethodName(TestMessageA event) { event.handled(this.getClass()); } @Handler public void handleEvent(TestMessageA event) { event.handled(this.getClass()); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/SimpleHandler.java000066400000000000000000000001501314636521700267510ustar00rootroot00000000000000package net.engio.mbassy.listeners; public interface SimpleHandler { void onMessage(Object msg); } mbassador-1.3.1/src/test/java/net/engio/mbassy/listeners/StandardMessageListener.java000066400000000000000000000026331314636521700310050ustar00rootroot00000000000000package net.engio.mbassy.listeners; import net.engio.mbassy.listener.Handler; import net.engio.mbassy.listener.Invoke; import net.engio.mbassy.listener.Listener; import net.engio.mbassy.listener.References; import net.engio.mbassy.messages.StandardMessage; /** * * @author bennidi * Date: 5/24/13 */ public class StandardMessageListener { @Listener(references = References.Weak) private static abstract class BaseListener { @Handler(priority = 3) public void handle(StandardMessage message){ message.handled(this.getClass()); } } public static class DefaultListener extends BaseListener { public void handle(StandardMessage message){ super.handle(message); } } public static class NoSubtypesListener extends BaseListener { @Handler(rejectSubtypes = true, priority = 4) public void handle(StandardMessage message){ super.handle(message); } } public static class AsyncListener extends BaseListener { @Handler(delivery = Invoke.Asynchronously, priority = -10) public void handle(StandardMessage message){ super.handle(message); } } public static class DisabledListener extends BaseListener { @Handler(enabled = false) public void handle(StandardMessage message){ super.handle(message); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/000077500000000000000000000000001314636521700231625ustar00rootroot00000000000000mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/AbstractMessage.java000066400000000000000000000025161314636521700271010ustar00rootroot00000000000000package net.engio.mbassy.messages; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * * @author bennidi * Date: 5/24/13 */ public abstract class AbstractMessage implements IMessage{ private Map handledByListener = new HashMap(); private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); @Override public void reset() { lock.writeLock().lock(); try { handledByListener.clear(); }finally { lock.writeLock().unlock(); } } @Override public void handled(Class listener) { lock.writeLock().lock(); try { Integer count = handledByListener.get(listener); if(count == null){ handledByListener.put(listener, 1); } else{ handledByListener.put(listener, count + 1); } }finally { lock.writeLock().unlock(); } } @Override public int getTimesHandled(Class listener) { lock.readLock().lock(); try { return handledByListener.containsKey(listener) ? handledByListener.get(listener) : 0; }finally { lock.readLock().unlock(); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/CountableMessage.java000066400000000000000000000002521314636521700272450ustar00rootroot00000000000000package net.engio.mbassy.messages; /** * * @author bennidi * Date: 5/24/13 */ public class CountableMessage extends AbstractMessage implements ICountable{ } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/ICountable.java000066400000000000000000000004671314636521700260610ustar00rootroot00000000000000package net.engio.mbassy.messages; /** * Interface analogous to IMessage. Exists to test more complex class/interface hierarchies * * @author bennidi * Date: 5/24/13 */ public interface ICountable { void reset(); void handled(Class listener); int getTimesHandled(Class listener); } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/IMessage.java000066400000000000000000000003321314636521700255200ustar00rootroot00000000000000package net.engio.mbassy.messages; /** * * @author bennidi * Date: 5/24/13 */ public interface IMessage { void reset(); void handled(Class listener); int getTimesHandled(Class listener); } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/IMultipartMessage.java000066400000000000000000000002361314636521700274250ustar00rootroot00000000000000package net.engio.mbassy.messages; /** * * @author bennidi * Date: 5/24/13 */ public interface IMultipartMessage extends IMessage, ICountable{ } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/MessageTypes.java000066400000000000000000000030421314636521700264350ustar00rootroot00000000000000package net.engio.mbassy.messages; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Enum used to test handlers that consume enumerations. * * @author bennidi * Date: 5/24/13 */ public enum MessageTypes implements IMessage{ Simple, Persistent, Multipart; private Map handledByListener = new HashMap(); private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public static void resetAll(){ for(MessageTypes m : values()) m.reset(); } @Override public void reset() { try { lock.writeLock().lock(); handledByListener.clear(); }finally { lock.writeLock().unlock(); } } @Override public void handled(Class listener) { try { lock.writeLock().lock(); Integer count = handledByListener.get(listener); if(count == null){ handledByListener.put(listener, 1); } else{ handledByListener.put(listener, count + 1); } }finally { lock.writeLock().unlock(); } } @Override public int getTimesHandled(Class listener) { try { lock.readLock().lock(); return handledByListener.containsKey(listener) ? handledByListener.get(listener) : 0; }finally { lock.readLock().unlock(); } } } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/MultipartMessage.java000066400000000000000000000002751314636521700273170ustar00rootroot00000000000000package net.engio.mbassy.messages; /** * * @author bennidi * Date: 5/24/13 */ public class MultipartMessage extends AbstractMessage implements IMultipartMessage, ICountable{ } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/StandardMessage.java000066400000000000000000000002461314636521700270740ustar00rootroot00000000000000package net.engio.mbassy.messages; /** * @author bennidi * Date: 5/24/13 */ public class StandardMessage extends AbstractMessage implements ICountable{ } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/SubTestMessage.java000066400000000000000000000002211314636521700267160ustar00rootroot00000000000000package net.engio.mbassy.messages; /** * * @author bennidi * Date: 11/22/12 */ public class SubTestMessage extends TestMessage { } mbassador-1.3.1/src/test/java/net/engio/mbassy/messages/TestMessage.java000066400000000000000000000003361314636521700262530ustar00rootroot00000000000000package net.engio.mbassy.messages; import java.util.concurrent.atomic.AtomicInteger; /** * * * @author bennidi * Date: 11/22/12 */ public class TestMessage { public AtomicInteger counter = new AtomicInteger(); } mbassador-1.3.1/src/test/resources/000077500000000000000000000000001314636521700172575ustar00rootroot00000000000000mbassador-1.3.1/src/test/resources/log4j.xml000066400000000000000000000007401314636521700210210ustar00rootroot00000000000000 mbassador-1.3.1/testNTimes.sh000077500000000000000000000003031314636521700161310ustar00rootroot00000000000000#!/bin/bash mvn clean for (( i = 1; i < $1 + 1 ; i++ )) do echo "Round $i" mvn test -o -Dtest=$2 exitcode=$? if [ $exitcode -ne 0 ] then echo "Error at round $i" exit fi done