The EventBus provides publish/subscribe event services for a single JVM.

The EventBus is especially suitable for Swing Applications, though it is usable in any context, even server-side. See the package description below for a complete description. The EventBus allows two components to communicate with each other without either of them knowing about the other (i.e., having a reference to the other).  It is an alternative to the tight coupling introduced by the typical Swing addXXXListener event mechanism.  When using the EventBus, the listener is subscribed to the EventBus instead of being added to a component.   The component that normally sends events to its listener list instead publishes events to the EventBus..  The EventBus takes care of routing events from publishers to subscribers.

This simple example prints "Hello World" using class-based publication.


class HelloThingy { //the same for all the examples public String getWorld() { return "World"; } }
class MySubscriber implements EventSubscriber { //EventSubscriber implementation public void onEvent(Object event) { HelloThingy helloEvent = (HelloThingy)event; System.out.println("Hello"+helloEvent.getWorld()); } }
class HelloEventBus { public static void main(String args[]) { MySubscriber subscriber = new MySubscriber(); EventBus.subscribe(HelloThingy.class, subscriber); EventBus.publish(new HelloThingy()); } }

Alternatively, this example can be coded using EventBus annotations and the AnnotationProcessor:

class MySubscriber {
   @EventSubscriber(eventClass=HelloThingy.class)
   public void printOutHelloWorld(HelloThingy event) {
   	System.out.println("Hello"+helloEvent.getWorld());
   }
}

class HelloEventBus { public static void main(String args[]) { MySubscriber subscriber = new MySubscriber(); AnnotationProcessor.process(subscriber);//makes a subscriber to call subscriber.printOutHelloWorld EventBus.publish(new HelloThingy()); } }

This second example prints "Hello World" using topic-based publication.


class MySubscriber implements EventTopicSubscriber { //EventTopicSubscriber implementation public void onEvent(String topic, Object event) { System.out.println(topic+" "+event); } } class HelloEventBus { public static void main(String args[]) { MySubscriber subscriber = new MySubscriber(); EventBus.subscribe("Hello", subscriber); EventBus.publish("Hello", "World"); } }

Alternatively, this example can also be coded using EventBus annotations and the AnnotationProcessor:



class MySubscriber { @EventTopicSubscriber(topic="Hello"); public void printOutHelloWorld(String topic, Object event) { System.out.println(topic+" "+event); } } class HelloEventBus { public static void main(String args[]) { MySubscriber subscriber = new MySubscriber(); AnnotationProcessor.process(subscriber);//makes a subscriber to call subscriber.printOutHelloWorld EventBus.publish("Hello", "World"); } }

Important: The EventBus uses WeakReferences by default, so this WILL NOT WORK since it uses an anonymous inner class:

   public static void main(String args[]) {
      EventBus.subscribe("Hello", new MySubscriber());
      //subscriber will likely get garbage collected - no Hello World!!!
      EventBus.publish("Hello", "World");
   }

However you can subscribe strongly instead:

      public static void main(String args[]) {
         EventBus.subscribeStrongly("Hello", new MySubscriber());
         //subscriber will not get garbage collected
         EventBus.publish("Hello", "World");
         //In real apps use unsubscribe to make sure you don't create a memory leak (a.k.a. - loitering object)
         EventBus.unsubscribe(subscriber);
      }
   

Though the term "Event Bus" refers to the entire package, the {@link org.bushe.swing.event.EventBus} class used above is a static wrapper around a global instance of an {@link org.bushe.swing.event.EventService}, specifically a {@link org.bushe.swing.event.SwingEventService} implementation.  MySubscriber subscribes itself to the topic named "Hello".  When main() publishes the String "World" (the data object of the event) on the event topic "Hello", the Event Bus calls MySubscriber and "Hello World" is printed.

An application may use multiple event services. There are two EventService implementations provided. One is the SwingEventService, which is useful for sending Swing events (The EventBus is a wrapper around a SwingEventService). The other is its parent, the {@link org.bushe.swing.event.ThreadSafeEventService}. As the name implies the ThreadSafeEventService can be used in multithreaded environments. The ThreadSafeEventService is the parent of the SwingEventService, so all provided EventService implementations can be used from multiple threads concurrently. The SwingEventService differs from the ThreadSafeEventService by ensuring that publications are posted on the Swing/AWT {@link java.awt.EventDispatchThread}.

The EventBus is intented to be very simple to use. There is no setup required and the API is fairly small. You do, however, need to be careful with memory management for subscribers. A couple of tips:

Two Analogies

The EventBus is a different motif for Swing design that some Swing developers find difficult to understand at first.  There are two analogies to the EventBus that may aid understanding.  The first is messaging systems like JMS where one process publishes messages on a topic name to other processes.  For example, a stock quote server may publish "$305" on a topic named "GOOG".  Client applications may subscribe to the "GOOG" topic and update their ticker when they recieve messages for "GOOG".  The JMS system decouples the clients from the server.  The server code does not need to explicitly keep track of how many clients are active, where they are located, whether the clients are still up and running, which ones are interested in "GOOG", etc.  The JMS system takes care of the details.  Similarly, the clients don't have to know whether the server is still up and running, where it located, etc.  All you need are publish() and subscribe() and a good JMS implementation.

Another analogy is a computer motherboard system bus.  Different components plug into the bus and receive and send messages to other componets on the bus.  You don't need to hook your CPU directly into each component in your computer, nor does your video card hook up directly to your monitor.  This layer of decoupling makes things a lot easier on device manufacturers, chip designers, computer manufacturers and computer owners.

The EventBus similarly solves many problems in Swing (or any appplication) by introducing this decoupling layer.

Advantages

Features

The Event Service is a centralized processor of application events local to the process (client).  It does not provide any processing of events outside of the process.  In other words it does not move events from a server to a client - it is not a solution for Event Driven Architecture in an SOA, or any other multi-tier problem.  However, the EventService is very useful as a mediator between server communications and the client code that needs it.  Each call from the server can be published on an EventService.  Any client-side component (Swing or non-Swing) can then subscribe to the client-side event and receive the updates.  This structure automatically makes your Swing app thread-safe in the incoming direction. In environments where the server provides frequent updates (say, every 2 seconds, depending on the speed of your client-side processing code), you should take care to throttle updates to the Swing EDT, perhaps coalescing many events into one. This can be done from the server side, of course, but the multithreaded EventService allows this to occur client-side as well assuming sufficient processing power on the client for the non-EDT threads (which is typically the case).

Usage

More usages will soon be available on the Event Bus wiki. See the main EventBus project page at http://eventbus.dev.java.net for a link to the wiki.

An example of topic publishing is shown above, below is an example of typesafe publication by Event class.  One nice usage of EventBus events is closing an application.  Typically you want to run some kind of routine when the user closes the application (an "Are you sure?" dialog, closing connections to servers, etc.). There are at least two places where an application is closed - the File->Exit menu and the JFrame's "X" button.  (or alt-F4 keystrok on Windows). The menu is an ActionListener and JFrame uses a Window listener. You could have the window listener call doClick() on the menu or call the menu's action and have the menu's action perform the work. This is less than ideal OO design, but is what Swing developers are often stuck with.  This gets even less elegant when another place in the application that closes the application - say an error catching routine (an AWTExceptionHandler or similar).

Instead, let's make thing more elegant and easy by using the EventBus

public class JFrame {
   public void initialize() {
   ...
   EventBus.subscribe(ApplicationClosingEvent.class, new EventSubscriber() {
       public void onEvent() {
           shutdown();
       }
   });
   ...
   }
   public void shutdown() {
      if (document.isModified()) {
          if (!userReallyWantsToShutDown()) {
             return;
          }
      }
      closeServerSocket();
      close();
   }

   private void handleWickedBadError() {
      EventBus.publish(new ApplicationClosingEvent());
   }

   private void createMenu() {
      ...
      //An aside - the best way to create menus is with the SwingEventManager! See http://sam.dev.java.net  :-)
      //The exit menu item's action is a special EventBusAction that fires an ApplicationClosingEvent() when clicked.
      MenuItem exit = new JMenuItem(new EventBusAction() {
         protected EventServiceEvent getEventServiceEvent(ActionEvent event) {
            return new ApplicationClosingEvent();
         }
      });
      ...
   }
   
Perhaps that doesn't look much better than calling shutdown() three times, but if this code is distributed three different files, it gets a little nasty. You can expose shutdown(), but then you are on your way to the Swing spaghetti dinner table.

Miscellany

The EventBus uses Apache Commons Logging so it will integrate with your logging framework - log4j, java.util.logging, etc. The hope was to have the EventBus not dependent on any other jar, but we felt this tradeoff was worth it.

A few of exception handling classes are included in the EventBus distributions. They don't have anything to do with the EventBus per se, but I simply didn't want to create another project for them. :-) SwingExcpetion is a handy exception when invokeLater() is called since the call stack that calls the invokeLater() can be captured (and is captured by the SwingEventService). The AWTExceptionHandler can plug into Swing to provide centralized handling of exceptions. Also included is a a dialog that handles excpetions, including emailing exceptions and copying them to the clipboard. It is a particularly handy when using the EventBus from multiple threads. When using the email feature of the dialog jdic.jar is required on the classpath and the associated binary (such as jdic.dll) are required in the java.library.path.