Blackboard event router

As I was writing on a Vaadin application that has a fairly deep component hierarchy, I noticed a problem with events.

In the application I’m writing, the user makes a selection for some data. This is notified to a component that shows details on that selection. But that selection can be changed also inside the details section itself, to show even more specific data, which resets the whole details component.

This filtering is done pretty deep inside the component hierarchy, and I was presented with the problem: should I pass the listener all the down the component hierarchy, or let the event bubble up, through all the intermediate components, each and everyone being a notifier/listener combination.

Since neither solution had “graceful” written anywhere on them, I was reminded of
blackboard systems
. So, I decided to write a generic one for everyone to enjoy. This is nothing Vaadin specific, so you can use it in any application, if you find the need.

As always, licensed under Apache License 2.0, and the source, example application and jar are all found in the
Directory
.

Once I have the time, I’ll write a bit of more documentation on its usage, but I hope the example application and the javadoc together will present with enough to get started with.

Embarrassingly, there was a serious bug related to inheritance of the Listener interface, so I have now updated the version to 1.0.1 – do not use the 1.0.0!. See original post for jar download.

I’ll make a note to write more unit tests, and more extensive example projects the next time.

Blackboard is now updated to 1.1.0, fixing an ugly memory leak.

As of 1.1.0 Blackboard requires
Google Collections
. That’s not necessary a bad thing, however, since the library is totally awesome, and should be used anyways.

Just out of interest - and pure laziness - what part of the Google Collections did you use?

The
MapMaker
, to
make a weak-valued map
.

I might try to get rid of that dependency, but since the implementations on Maps seem very non-trivial, I haven’t had the guts to try and implement a bug-free map. I have also considered bundling just what I need with Blackboard, but Google Collections seems to share code heavily, so the internal dependencies seem to be somewhat deep.

Having Google Collections as a dependency seemed a good compromise, I didn’t have to sweat with anything, and the client project gets an excellent library to boot :).

I’ve written a wiki entry on how Blackboard works and how it should be used:
http://wiki.github.com/wolfie/Blackboard/

IMHO, EventBus is a better name than Blackboard, because essentially, this does the same thing as GWT’s HandlerManager, which is often referred to as EventBus.

Well, it does sound better, at least. Although, the last thing I’d want to do is to cause confusion with existing concepts. Even worse, I just noticed another project officially called
EventBus
, which apparently does what Blackboard does, but with a static access class.

I’ll probably stick with Blackboard for now.

After looked into the source, I noticed that there is a one-to-one restriction on Listener class and Event class pair. It seems to me that this is a limitation and also an extra step. Limitation in that one listener class can only handle one type of event, and one event can only be handled by one type of listener class. In real usage, it is very common that one class handle multiple types of events and one type of events be handled by multiple types of listeners. And when setting up listeners, you will first call register(listener.class, event.class) and then addListener(listener), which ideally should be achieved by just one line, like addListener(listener, event.class).

I would suggest something like the following. This way, one listener class can handle multiple types of events; and it’s done with annotation, which means there won’t be any boilerplate code.


class MyEventListener implements Listener {
    ...
    @ListenerMethod(EventA.class)
    public void onEventA(EventA event) {}

    @ListenerMethod(EventB.class)
    public void onEventB(EventB event) {}
    ...
}


This became a longer reply than I intended. Tl;dr -summary at the bottom.

In a way, you are right. There is a one-to-one relationship on who listens on what kinds of events, indeed. However, this isn’t restricted to instantiable classes, but can be applied also to abstract classes and, as
the wiki
encourages, interfaces (as shown in the
unit tests
). This is kind of a “works as intended” -situation. The thought behind this is that this kind of enourages/forces you to write clear and descriptive interfaces, allowing you to use the “type hierarchy” feature in IDEs to find out all listeners for an event. Also, the register()-method is designed to be called in the same class where the Blackboard instance is stored.

This would lead to the intended use pattern: “register()”-calls are all bunched up somewhere near the “new Blackboard()” call, while each implementer of Listener subinterfaces calls the “getBlackboardFromSomewhere().addListener(this, MyEventListener.class)” on their own.

I happened to notice that my
example application
doesn’t reflect this at all, and I’m sorry about this. It is currently done the quick’n’dirty way, which is explicitly discouraged by the wiki. I should fix the example asap.

Now, the question remains, are these good design decisions. Maybe, maybe not. It has thus far worked well in a somewhat complex project, but it does accumulate a lot of interfaces and classes. But I have already thought of some design changed: Removing the Notifier interface, since it’s essentially dead weight and doesn’t bring any added value to the table. I have also thought of disallowing the registering of classes, and only allow interfaces (not sure what to do with abstract classes).

Your suggestion has given me something to think about, for sure. The more I look at it, the more I’m starting to like it. Your approach might do away with the Listener-interface, too, since the value would be in the annotations, and the interface would give no guarantees of anything. Who listeners to what can still be inspected by the IDE, since you can check for references to the Event classes instead.

The two downsides I need to think about are that it would break the current API pretty badly and you would lose the requirement of properly done interfaces, which you could call manually on demand. Neither seem all that problematic, though, since the API doens’t have all that much of a circulation yet (~50 downloads), and Blackboard should not be used for close proximity message passing, but for more of a broadcasting kind-of-thing.

tl;dr: I might need to rethink some things, and the example application needs to be fixed anyways.

I’ve implemented the EventBus proposed in my earlier post with annotations. It’s much simpler to use, see example below:

// Defining an event class
@Event
private static class EventA {
	private String message;
	
	public EventA(String message) {
		this.message = message;
	}
	
	public String getMessage() {
		return message;
	}
}

// Defining an event subscriber, note that this subscriber can subscribe multiple events at the same time
public static class Foo {
	@EventSubscriber(EventA.class)
	public void onEventA(EventA event) {
		System.out.println("Foo.onEventA() - " + event.message);
	}
	
	@EventSubscriber(EventB.class)
	public void onEventB(EventB event) {
		System.out.println("Foo.onEventB() - " + event.message);
	}
}

// Adding the subscriber
Foo foo = new Foo();
eventBus.addSubscriber(foo);

// Firing the event
EventA eventA = new EventA("First event");
eventBus.fire(eventA);

The code is functional, let me know if anyone is interested, I’ll find a way to share the code.

I would interested in seeing your example code using the event bus.

Please see the source attached.
11282.zip (5.64 KB)

Could you please compare your solution with
Mycila events

With Mycila I have one major problem - I can’t use soft references, cause soft listeners are droped after first request (for unknown reason; maybe it’s grails-related issue… I don’t have enouth time to dig it).
I need “event bus” to listen database updates (singleton dao), so one bus instance should broadcast to all application instances. Without soft references avoiding mem leaks is quite tricky.

Will Blackboard help me in my use-case?

Or, maybe, you could give me an advise about soft listeners dropping problem in Mycila.

I’ve never used Mycila, but it seems spiffy. Their approach is less coupled (with string event names) and are focused on performance. Blackboard uses interfaces to give a more direct coupling between events and their subscribers (“listeners” in Blackboard). Blackboard’s events are blocking, while Mycila seems to be asynchronous by default. They seem to try solving the same problem but with quite different approaches.

As I’ve mentioned in
the wiki
, Blackboard’s memory handling is done with soft references, so probably not. Java and explicit memory management don’t go together too well, it seems.

Hi Henrik,

Thanks for the library.

I would like to ask you about the weak-references. Why use them? Well, I’m gonna tell you my case and maybe I’m even wrong to grasp the idea that the weak-reference is the culprit.

I am initializing Blackboard as suggested in the application start. So, after using my app for a while, I noticed my events were not being fired anymore. So, I began investigating and it seems, my listeners have been “unregistered” from the listeners map.

Don’t you think we should guarantee that events are being triggered successfully all the time?

Thanks a lot,

Marcos Alcantara

You’re absolutely right, and that’s a bug. I weak references would work in a slightly different way, but evidently, they didn’t.

I do a have a slightly tweaked version of Blackboard on my computer as we speak, but it’s currently a bit unpolished, thus I haven’t published it yet. I have plans to do some 10%-time on Friday, so expect some kind of update then (if I have enough time, that is).

Blackboard 2.0.1 is
now available
.

The changes are pretty extensive. Compared to 1.2.0, they’re the following:

  • API is incompatible with eariler releases
  • Notifier is removed completely
  • com.github.wolfie.blackboard.Event is now an interface
  • Blackboard.register() now accepts only interface-Listeners
  • The example application is cleaned up
  • Dependency for Google Collections is removed
  • Registering abstract Events is disallowed

I’ve tried to simplify the API, while making it harder to screw up by having bad code.

The
documentation
has been updated accordingly. I’d also recommend you to check out
the example application
, to get a better grasp on how using Blackboard looks like now.

Hi Henrik,
did this solve the problem with weak references and disconnecting listeners ?

Im actually thinking to embed BB to TPT, it seems using BB to handle various app events is much much simplier and easier.

Oh, right, I should’ve perhaps mentioned this in the previous post. BB uses hard references only now. No (un)natural object decay, and you get to keep your references throughout a BB’s lifetime, unless you remove a listener manually.

PS: there should be some kind of download sharing program in the Directory :slight_smile: