Recently, I started wondering how I could improve the TestNG listener architecture.
TestNG exposes a lot of different listeners, which users can specify before starting a test run (using either of the command line, ant, Maven, testng.xml or programmatically).
These listeners tell you whenever:
- A test succeeds, fails or is skipped.
- A test method is about to be called and when it returns.
- A configuration method starts or finishes (there are five types of each: suite, test, class, method and group, and you have a before and an after event for each).
- A reporter should be notified to update its results (either at the end of the suite run or after each test methd).
All these listeners are captured by different interfaces and they have evolved somewhat organically over these past six years as the needs and requests emerged, showing some overlap in functionalities and also requiring the awkward interface evolution that Java imposes (exhibit A and exhibit B).
One possible solution to such a problem is a message (or event) bus.
Message buses have been around for a very long time and most of my PhD thesis revolved around their usage and their impact on distributed applications. I even wrote one for my PhD called Koala Talk. This was around 1993, almost twenty years ago.
The de facto standard in the Java world is the JMS specification, which has been implemented in many products such as ActiveMQ or RabbitMQ. Both the specification and implementations have been battle tested and proven to be of great usefulness for today’s software.
However, JMS is overkill for what I need to do. First of all in terms of functionalities, but also more simply because I need a local software bus, one that will always be passing messages within the same JVM. No network is necessary.
The message bus model brings a lot of simplification to a listener heavy framework such as TestNG. Instead of having to decide which interface and which method are applicable when, all TestNG needs to do is publish events as a test run is progressing. Where these events get handled and who they eventually reach is resolved outside of the TestNG engine.
Another benefit is that looking at the system in terms of events will allow to remove quite a bit of overlap. For example, in TestNG, you can be notified both when a suite finishes and when your reporter should start doing its work (generating reports). Most of the time, these events coincide.
I started sketching out what such an API could look like and I came up with the following very early draft:
public class App { @Subscriber public void event1(NotifyEvent ne) { System.out.println("event1: " + ne); } }
MessageBus mb = new MessageBus(); mb.register(new App()); mb.post(new NotifyEvent("notify"));
Receivers describe the kind of events they are interested in and publishers simply post these events. With these ideas in mind, I started looking around to see if anything like this exists and I quickly came across EventBus, by Michael Bushe.
EventBus turns out to be remarkably close to what I had in mind, including annotation support and type based dispatching. EventBus also supports all kinds of other mechanisms, some I had in mind (string based publishing with regexp matching, event inheritance) and a few others I didn’t think of (vetos).
EventBus seems to be very extensive, very well designed and tested (nice job, Michael!), but probably too big for what I need. It also contains quite a few ties to Swing because it apparently started as a support library for Swing applications that publish events (something that Swing developers have to deal with all the time). Admittedly, there is a portion of EventBus that seems to be graphic independent, but I haven’t been able to really understand its full extent and whether it’s possible to carve it out at all (I have no interest in dragging in Swing dependencies).
Interestingly, it’s the only library of that type that I was able to find, so before I dig further, has anyone heard of a framework allowing the kind of simple intra JVM publish/subscribe functionality I’m looking for?
#1 by Igor on July 26, 2010 - 11:04 am
Google Web Toolkit has an implementation of Event Bus. Not quite sure if it is what you are looking for (since it is targeted for JavaScript and doesn’t have annotation support or fancy dispatching) but it is type-safe and easy to use. Just thought you might be interested to take a look at it.
Regards,
Igor.
#2 by Guillaume on July 26, 2010 - 11:39 am
CDI in Java EE 6 has something very similar.
Publisher and subscribers are completely decoupled.
In the publisher, you simply ask to be injected with an Event (T being the event type).
In the subscriber(s), you have an @Observer annotation that you place on a method parameter.
http://www.adam-bien.com/roller/abien/entry/java_ee_6_observer_with
BTW, it is not tied to Java EE 6 and can be used standalone (see the Weld implementation reference).
#3 by nullin on July 26, 2010 - 2:45 pm
Sounds like a good idea but I don’t think it can replace the existing listener support. Even though the interfaces are still all called listeners thats not exactly how they are being used.
This is because the code calls the methods on these interfaces synchronously. So, it quite easy for many folks to have started depending on the fact that once a method in a listener is invoked, they can do an operation without being worried about the test execution continuing.
#4 by Adam on July 26, 2010 - 8:58 pm
HornetQ can be embedded:
http://hornetq.blogspot.com/2009/09/hornetq-simple-example-using-maven.html
#5 by Cedric on July 26, 2010 - 10:20 pm
Nalin:
The message bus system would still be synchronous, so that wouldn’t be a problem. As an option, it could offer to run publishers and subscribers on different threads down the road, but that’s not a requirement for v1.
#6 by Mohan Radhakrishnan on July 26, 2010 - 11:17 pm
Dojox wires had support for publish/subscribe. Don’t know if it is possible to use it with the Javascript engine in Java ?
#7 by Josh Long on July 26, 2010 - 11:30 pm
Very good post! Note that RabbitMQ does not – in fact – implement JMS, but AMQP. (which I’m sure you knew…)
#8 by Tim on July 27, 2010 - 5:24 am
Very interesting! I’m a huge proponent of the message bus system and would love to see a good clean implementation.
But regarding your JMS and RabbitMQ comment, I’m not sure that I understand that one. Aside from a hacked OpenAMQ JMS client, I don’t know how you can talk to RabbitMQ using JMS since JMS != AMQP. Am I missing something here? Would be great if I could use JMS interfaces with RabbitMQ without a hacky solution.
#9 by Derek Young on July 27, 2010 - 6:26 am
Spring provides some services like you describe, for example with the ApplicationListener interface. There’s also the Spring Integration project that does messaging both within and between applications.
#10 by vinays on July 27, 2010 - 11:20 pm
I think Spring Integration is very nice framework and you can have channels working as listenres subscribers and easy to use.
#11 by David Bernard on July 28, 2010 - 10:51 pm
I thinks mycila-events should match your need (lighter than EventBus, annotation support but use of annotation is not mandatory, …). The authors did the same analyze as yours.
http://code.google.com/p/mycila/wiki/MycilaEvent
/davidB
#12 by Ramesh on July 30, 2010 - 8:59 am
How about http://www.hammurapi.com/dokuwiki/doku.php/products:event_bus:start
#13 by wanderer99 on August 19, 2010 - 3:13 am
Check out the OSGi whiteboard pattern. I’ve found that this is really the most flexible mechanism for implementing the listeners and subscribers. In this model, listeners simply place themselves in a known location and it’s up to publishers to decide who gets each events.
#14 by Mike Rettig on August 22, 2010 - 8:29 am
http://www.jetlang.org
Jetlang only requires the JDK. The simple clean api can be used for basic messaging scenarios as well as highly concurrent performance critical messaging platforms.
Mike Rettig
Jetlang Developer
#15 by Ken on September 27, 2010 - 8:26 am
Mule esb has a local bus built in called the vm transport that does exactly what you describe.
#16 by Bruno Ranschaert on June 3, 2011 - 12:18 pm
I created an implementation following Cedrics ideas which is available on GitHub https://github.com/branscha/lib-messagebus.
#17 by myles on October 4, 2011 - 1:22 am
the newly released version of Guava seems to contain an implementation:
http://docs.guava-libraries.googlecode.com/git-history/v10.0/javadoc/com/google/common/eventbus/package-summary.html