Use dynamic proxies to create a simple, powerful event bus (Part 2)

In part 1 of the event bus series we discussed implementing a simple and powerful event bus using just three classes. If you haven’t read it yet, I strongly recommend you read it first.

Use dynamic proxies to create a simple, powerful event bus (Part 1)

In this blog we’ll build on part 1 by adding several important features to the event bus to make it production ready.  Since these features are fairly well contained, you can start with the section that most interests you, download the code, or view the project on GitHub.

Event Filtering

Event filters help reduce noise by allowing listeners to automatically ignore events they would normally receive. This is accomplished by including a new EventFilter object alongside every listener added to the bus.

The EventFilter class has a single abstract allow(event, listener) method that returns true if the event should be delivered to the listener.

Java Event Bus - Filter Classes

Several filters are already are part of the bus, including EventSourceFilter. This filter only allow events that were produced by one of a set of sources.

In addition to the new EventFilter interface and the addListener update, a few other changes are needed to handle filters:

  1. A new EventListenerStub class to hold a listener and filter together.
  2. The eventListeners field on EventBus is converted from a Set<? extends EventListener> to a List<EventListenerStub<? extends EventListener>>. We swap Set for List because we can no longer depend on the listener’s object identity to prevent duplicate listeners in the set (now that it’s wrapped in an EventListenerStub).
  3. The bus’ deliverEvent() method calls EventFilter.allow()to check if the event should be delivered.

Here’s a quick example of EventSourceFilter in action.

Event Topics

It’s not always ideal for us to filter by the event’s source. Sometimes it’s more convenient to filter by a value known to both publishers and listeners. That’s where topics come in. Topic values can bet passed as an optional parameter while obtaining a publisher from the bus. Every method call made on that publisher will include the topic value. Listeners can watch for specific topics by including a TopicFilter when registering with the bus. Topics can be any java.lang.Object or subclass we like. I recommend enums because they are IDE and refactoring friendly, but a string or any object will do.

Publishers

A few small changes on the publisher side will handle the registration and propagation of topics for us. First, both the Event and EventSourceInvocationHandler classes will track topics. EventSourceInvocationHandler will have it set during construction and pass it on to every event it creates.

Second, the bus’ getPublisher() method will be overloaded to take in a topic value and set it on the EventSourceInvocationHandler.

Listeners

The TopicFilter class allows listeners to receive events matching one or more topic values. The implementation uses exact matching, but if that’s too simplistic for your case, it should be easy enough to implement your own hierarchical or pattern matching filter.

Here’s an example that uses enum topics to publish and filter events.

Untyped Listeners

Some use-cases require that we listen to all events passing through the bus regardless of their type. Audit, monitoring, or even debug requirements might call for such a feature. In these special cases we can register an UntypedEventListener with the bus. These listeners are notified in the form of the Event object after all typed listeners have received their notifications.

EventListenerStub Refactoring

Now that we’re handling both typed and untyped listeners, EventListenerStub will undergo refactoring to reduce code duplication. First, EventListenerStub is divided into three classes. The original EventListenerStub and two inner subclasses: Typed and Untyped. The new subclasses are responsible for providing the delivery logic for their kind of listeners while allowing EventListenerStub to handle filtering and other common behaviour.

Java Event Bus - Listener Classes

Just as before, the Typed subclass invokes the original called method on each listener.

While the Untyped subclass calls the onEvent method on untyped listeners.

EventBus Refactoring

The event bus gets a few upgrades.

  1. The old eventListeners field is renamed to typedListeners to compliment the new untypedListeners field.
  2. Both versions of addListener (typed and untyped) take care to add the correct subclass of EventListenerStub to their lists.
  3. The bus’ deliverEvent method now delegates to the deliverEvent method on each stub to ensure the correct delivery behaviour.

Here’s an example of an untyped listener watching all events on the bus.

Exception Handling

So far we haven’t said anything about exceptions. If you’ve looked at the source code from part 1, you’ll see that exceptions were caught, wrapped in RuntimeExceptions, and then rethrown. This saved us from the extra coding to declare checked exceptions, but did nothing to actually handle them. What we need now is an exception handling strategy that:

  1. Handles all exceptions regardless of which thread throws them
  2. Handles exceptions for both typed and untyped listeners
  3. Doesn’t require knowledge of the bus’ internal workings
  4. Is decoupled from listeners
  5. Is simple to use

Exception Events

We can accomplish all of these requirements by simply leveraging the bus itself. By letting the event bus publish exceptions like any other event, we gain all of its advantages (decoupling, threading, filters, etc.) for the price of a small change.

Java Event Bus - Exception Classes

The new ExceptionListener interface is used for both publishing and observing exceptions. The bus holds a publisher reference which it exposes through its handleException method. Since the bus delegates the actual event delivery to EventListenerStub, that class is also responsible for reporting exceptions to the bus by calling handleException. The handleException method takes care not to introduce infinite loops by not publishing exceptions for events where it was the publisher.

This example prints each exception’s message along with the event class’ name and method.

Garbage Collection

Memory leaks are a real concern when using the observer pattern. Observers that are never unregistered may never get garbage collected and your application’s memory could continue to grow until it eventually fails with an OutOfMemoryError. Memory leaks are arguably even more of a concern when working with event buses because they are responsible for holding on to all observers for all subjects. If a bus lives for the entire run of your app while holding on to every single all observer, we could get that OutOfMemoryError even sooner.

Soft References

The java.lang.ref package is Java’s answer to allowing objects to be garbage collected, even while there are still live references to them. The key is to wrap those objects with one of the classes in this package. In the event bus, we now wrap each listener in a SoftReference. This allows listeners to be automatically garbage collected when memory gets low, but now introduces a level of indirection to our code. We chose to use SoftReference over a WeakReference because they are garbage collected less aggressively by the JVM. Since most listeners tend to be anonymous inner classes, only referenced by the bus, I’d hate to see them disappear while the system is still flush with memory. (For more on Java references, read Ethan Nicholas’ classic blog Understanding Weak References. )

EventListenerStub

The code that manages listeners through soft references can e found in EventListenerStub and its two subclasses — Typed and Untyped. EventListenerStub wraps the listener and ensures the getter method is used for all access. The Typed and Untyped subclasses now perform null checks (in case listeners were garbage collected) before actually delivering events.

Reference Clean-up

You might have noticed the ReferenceQueue parameter in EventListenerStub‘s constructor. Reference queues allow the garbage collector to inform us when the objects in the soft references are actually released. The exact details differ for each kind of reference, but generally the garbage collector will put released references into the queue for us. We simply have to poll the queue periodically to be notified. Using reference queues allow us to be very efficient at detecting garbage collection, it saves us from having to test every listener reference to do a proper clean-up.

The event bus now holds two reference queues for garbage. One queue for typed listeners and the other for untyped. The queues are passed into the EventListenerStub subclasses along with each listener during registration. Finally, each event delivery now finishes with a call to the bus’ cleanupGarbage() method which polls the queues and removes anything found there. Although cleanupGarbage() returns very quickly when queues are empty, you could probably come up with an even better approach than checking every time.

Here’s an example that shows soft references surviving a forced garbage collection (System.gc()), but not a true memory exhaustion (allocating 100 GB or RAM). This example was run with the following configuration:

  • Sun/Oracle’s JDK 1.6.0.10 running in Eclipse 3.5
  • Windows XP Professional, Service Pack 3
  • 3 GB or RAM
  • Swap file disabled

Thread-local Events

In the Untyped Listeners section we saw how to implement a bus wide listener. This gave us access to the raw Event object containing things like the event source, listener interface, and invoked method. Unfortunately, this forced us into choice. Either we listen for typed events and used whatever parameters the listener interface defines or go untyped and have the full Event object. Going untyped means extra filtering coding to select the exact event we’re interested in. Fortunately, we don’t have to make that choice.  We can continue to work with typed listeners and access the full event using a thread-local when needed.

Thread-locals

Thread-locals allow us attach values to the current thread then access those values from any method running on that thread. This saves us having to modify methods to explicitly pass values around. For the event bus, this means linking the Event object to the delivery thread which we can then access anywhere, including inside a typed listener.

The event bus contains all of the changes:

  1. A new thread-local field for the event (currentEvent)
  2. A getter method for use anywhere (as long as it’s in the delivery thread)
  3. Setting and removing the current event during delivery

This example uses the thread-local’s event to display the name of the invoked method.

Next Time

That’s a wrap. We’ve covered a lot of ground in this blog. Hopefully, I left you with a few ideas you can use in your own event bus or applications.  Do you have a question or suggestion to improve some of the ideas we’ve discussed?  Leave a comment, I’d like to hear about it.

In part 3 of the Event Bus Series, we’ll discuss implementing the event bus using GWT (Google Web Toolkit) Generators. Stay tuned.

Download

The event bus download contains the entire source code (including Eclipse project). Alternatively, you can view the project on GitHub. The source code is licensed under the terms of the Apache License, Version 2.0.

 

Java Exception Tracking

If you enjoyed this, I’ve got an exception tracking tool for Java coming out. Sign-up at StackHunter.com to be notified when it does.

About Dele Taylor

Dele Taylor is the founder of NorthConcepts.com, creators of Data Pipeline -- a data migration library for Java. You can follow him on Twitter, G+, and LinkedIn.
Dynamic Proxies, Event Bus, Java | permalink

Comments

  1. Brandon

    You should consider putting the code up on github so others can browse/contribute to it (I’d be willing to help you with this if you’d like). Also since this is generally useful library code it’d also be useful if you were to publish it to a centralized maven repository so that others can use it more easily.

    • Dele Taylor

      Brandon – I really like your ideas to host the event bus as an open source project and distribute it using Maven.

      GitHub and Git are new to me, but I’ve heard good things about both so I’m looking forward to learning them. I’m going to start on this today, but it may take me a couple days set things up.

      Of course your help would be greatly appreciated and I’ll email you to discuss.

      Thanks for your suggestions and offer.

      Dele

      • Brandon

        Sounds great, email away. I played a little bit with maven earlier today, seemed really easy to get your code building in it.

  2. I agree with Brandon. This code is very nice and useful. People will benefit much more from it if you publish it to a centralized maven repo.

  3. Mariano Ortega

    Nice work! This looks like pretty neat.

  4. open source jdon framework is a Event framework based on Disruptor, it use annotation for event bus. http://www.jdon.org

  5. Pingback: How to build a simple GWT event bus using Generators | North Concepts

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">