Important Notice - Forums is archived
To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.
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?
Risto Yrjänä: 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.
Tong Wang: IMHO, EventBus is a better name than Blackboard
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) {}
...
}
Tong Wang: After looked into the source, I noticed that there is a one-to-one restriction on Listener class and Event class pair.
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).
Tong Wang:
class MyEventListener implements Listener { ... @ListenerMethod(EventA.class) public void onEventA(EventA event) {} @ListenerMethod(EventB.class) public void onEventB(EventB event) {} ... }
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.
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.
Vyacheslav Rusakov: Could you please compare your solution with Mycila events
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.
Will Blackboard help me in my use-case?
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.
Dmitri Livotov: did this solve the problem with weak references and disconnecting listeners ?
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.
Dmitri Livotov: Im actually thinking to embed BB to TPT, it seems using BB to handle various app events is much much simplier and easier.
PS: there should be some kind of download sharing program in the Directory ^_^
Hi,
I like the Blackboard tool a lot, but one thing bugs me about it - why do I have to annotate the listener method with @ListenerMethod on the listening class? The listening class has already implemented the interface and registered itself, shouldn't that be enough?
Thanks,
Mark
has been updated again!
I've added some magic recognition code in there, to allow people to skip the blackboard.register(listener, event); routine, allowing Blackboard to automagically scan for existing Event/Listener pairs.
First of all, I've added a new annotation – @ListenerPair – which can be given to your Event class. It takes the corresponding Listener interface as its argument. This tells Blackboard how the listeners and events should be paired together. You don't have to use the annotation, though, if you follow a new convention: Write each Listener as a public inline interface to its corresponding Event. Example:
@ListenerPair(MyListener.class) // this is not necessary, in this inline case
public class MyEvent implements Event {
public interface MyListener implements Listener {
@ListenerMethod
public void react(Event event);
}
}
So, if your events and listeners are structured this way, you don't have to use the @ListenerPair annotation (although it's recommended, to make it explicit).
The automatic registration can be done in one of two ways:
1) As soon as you have instantiated the Blackboard instance, call blackboard.discover(). This sends Blackboard to an epic journey of magic code, which tries to find all listener/event pairs (according to the format mentioned above) in and underneath the current package. You can modify which package acts as a root, by using blackboard.discoverFrom(Class) instead, where Class is any class in the desired root package. This should work most of the times, but since it's a bit magic, it can fail terribly. YMMV
2) Skip the "discover()" call, and just don't do anything. Each time you add a listener to Blackboard, that will be paired with a suitable Event just in time. This format requires the code style convention (with the inline interfaces), and annotations will not help you here.
So, here's some lazy coding improvements. You are still allowed to register each pair manually (which will disable any magic thingies, by the way), and that's always the most reliable way to register them. I have some ideas about 2.2.0, but those might need to wait for quite some while.
The discover() part is a little frightening, but the automatic connection on adding a listener sounds good.
I was re-reading this thread. The major use case I have for the blackboard is event propagation in a push() context.
This means that I often have listeners that should just go away when the user closes a window closed, and they must also be re-established if the window is reloaded. This is currently painful, as it requires carefully maintaining a cleanup routine invoked on the window close, and a similar routine on reload (URI handler).
Jean-François Lamy: The discover() part is a little frightening, but the automatic connection on adding a listener sounds good.
Yeah. It's just a helper function that I haven't tested all too thoroughly. You're free to use it, but you're not forced to. I'm interested in hearing any experiences
The major use case I have for the blackboard is event propagation in a push() context.
This sounds like an intriguing feature. I'll look into this. You could of course do the addlistener thing into a constructor, and garbage collect it manually whenever the window is closed. But, I'll see what I can help here with.
Glorious news: There's yet another release, now with even more heuristics!
@ListenerMethod is can now be deducted by Blackboard, thanks to even more magic code. If there really is one logical choice for the method, that will automatically be picked, in the following priority:
- Only one method has been annotated with @ListenerMethod
- The interface only has one method
- The interface has several methods, but only one accepts the type of event we're trying to register.
Enjoy the lazy coding!
Hello,
First of all, thanks for a great add-on for Vaadin! But...it seems to be impossible to use BlackBoard with GAE as it does not implement serializable. Could you please tell if it will be implemented someday or somehow possible right now?
Alexander V Pchelintsev: First of all, thanks for a great add-on for Vaadin!
You're very welcome! I'm glad you like it
But...it seems to be impossible to use BlackBoard with GAE as it does not implement serializable.
That's correct. Unfortunately, the java.lang.reflect.Method class, which is stored inside Blackboard is not serializable either. Therefore, in case I've understood it correctly, I can't guarantee the whole Blackboard being serializable and thus I can't put any of it as serializable. I'm sorry :/
Hi there,
I'm trying to use Blackboard on a Vaadin based portlet project. I'm facing some problems with Blackboard 2.1.1: when I fire() an event, it somehow stays somewhere in memory, and gets replayed each and every time another event of the same type is fired. I tried to debug your code a bit, but the latest source code from Github is not really the 2.1.1 one. Where can I find the sources?
Thanks in advance for your help,
c.
Hi!
Carlo Sciolla: When I fire() an event, it somehow stays somewhere in memory, and gets replayed each and every time another event of the same type is fired.
That's very weird. I've never had that kind of trouble. I've never tried Blackboard within a portlet, but it sounds highly unlikely that it would have something to do with that…
Carlo Sciolla: I tried to debug your code a bit, but the latest source code from Github is not really the 2.1.1 one. Where can I find the sources?
Hm. According to the package I just downloaded from the Directory (http://vaadin.com/directory#addon/blackboard), the sources are included in the jar (as they should be). If everything else fails, unzip the jar, and put the source files in your classpath (or something similar). Maybe you're experiencing some classloader problems, which might explain what you're experiencing (although, classloader problems often cause unforeseen consequences)
I'm afraid that I can't help you more with this issue :(. If the issue doesn't go away even after you've debugged it, I'd love to help you, given a bit more details about your problem.
PS: unfortunately, I'm unable to check my email constantly, and I seem to forget to check my email manually. So my replies might take some days…
Hi!
I'm using blackboard and it was working very fine until yesterday :)
I disabled the explicit registration of events to use the magic registration and then it stopped working for one very particular case.
I have the following class that should react to 2 events managed by the blackboard (in bold):
public class TableChangesTable extends Table implements [b]TableChangesListener[/b], [b]UseCaseClickListener[/b], I18NListener {
public TableChangesTable(){
getBlackboard().addListener(this);
}
}
If I look at the code to register the listeners on the addListener method of the blackboard I can see this:
Collection<Class<? extends Listener>> registeredListenerClasses = getRegisteredListenerClasses(listenerClass);
if (registeredListenerClasses.isEmpty()) {
boolean success = false;
if (magicRegistration) {
success = magicRegisterAllListenerInterfacesIn(listener);
}
if (!success) {
throw new NoMatchingRegistrationFoundException(listenerClass);
} else {
registeredListenerClasses = getRegisteredListenerClasses(listenerClass);
}
}
The magic registration should register both listeners. The problem is that in this particular case I already registered the UseCaseClickListener previously in my application, only the TableChangesListener in not registered yet and should be registered.
So when the blackboard calls this line : Collection<Class<? extends Listener>> registeredListenerClasses = getRegisteredListenerClasses(listenerClass); it can find the UseCaseClickListener, that means that the registeredListenerClasses is not empty and it does try to register the other one...
I hope I was clear enough :)
If you have an idea or a fix then please tell me :)
Thomas
Hi Henrik,
I just started to use your EventBus. Works great - Thank you very much.
One improvement could be the logging.
I want to log with Log4J - there is no easy possibility for that at the moment. I don't want to log everything to System.out.
An easy solution could be to make your Log.logTo(PrintStream)-method accessible. I could pass in my own simple stupid PrintToLog4jPrintStream or something like that.
A more complex, but cleaner solution could be to use commons-logging.
Thank you very much,
Daniel
Hello,
Thank you very much for this add-on, the event bus management is easier but I just wanted to know how to build clean classes for the interface and the events.
interface extends Listener
void deleteUser(event1)
void addUser(event2)
Problem : I got a runtime error saying I can't register the same method ??
Btw, i don't understand very well the @ListenerMethod annotation, can you provide me some informations about it please ?
Thx !
Hi,
I use Blackboard with Maven and since 2.2.0 i cannot get the correct sources for the downloaded .jar
Has somebody ideas to resolve this issue?
Update: I got an error if i want debug blackboard: Absent Line Number Information.
thanks
Andre
A support to register an error handler for catching exceptions from listeners would be a great improvement. Now you need to remember to code try-catch block in every listener otherwise Blackboard eats the exceptions and prints stacktraces to console.
Such an addon is very important to have clean code in complex UI's.
Currently we use the EventBus from guava (https://code.google.com/p/guava-libraries/wiki/EventBusExplained) and it works great.
An important design decision is if you should have a global Event Bus instance, an instance per session or an instance per Vaadin UI instance. We use one instance per UI instance to deliver events within a UI.