How to build a simple GWT event bus using Generators

In his Google I/O session Best Practices For Architecting Your GWT App, Ray Ryan discusses the benefits of using an event bus in GWT (Google Web Toolkit) applications. Inspired by this talk, I decided to try my hand at building a simple GWT event bus modeled after our pure java event bus.

See the previous articles:
Use dynamic proxies to create a simple, powerful event bus (Part 1)
Use dynamic proxies to create a simple, powerful event bus (Part 2)

Using the event bus

If you’ve followed along with the previous event bus blogs, you already know what I mean by simple – simple to use. We want an event bus that doesn’t require a fleet of event classes or fireXXX methods.

We want to call a method, like newSearch("GWT event bus"), and have it automatically invoked on all observers as if we’d called each one ourselves. We want method multicasting. As you can see in the code above, we use the same SearchListener interface to both observe newSearch events and publish them.

GWT generators

In the original event bus we used reflection, specifically, we used Java’s dynamic proxies to accomplish the publisher-listener magic. GWT doesn’t have anything like this, but it does provide hooks into the compiler for code generators. Basically, we can generate the publisher code at compile to perform the same function dynamic proxies did at runtime.

GWT Compile Steps

Code generation happens (it seems) during GWT’s precompile phase. The actual compiler hook is triggered by a combination of a GWT.create() call in the Java source and a generate-with element in the GWT module XML file. The GWT.create method takes a class literal to instantiate – GWT.create(PublisherFactoryRegistry.class) in this case. The generate-with element tells the compiler that EventPublisherGenerator will create the Java source that extends (or implements if it were an interface) PublisherFactoryRegistry.

The compiler treats the Java source emitted by EventPublisherGenerator like any other class and optimizes it, converts it to JavaScript, obfuscates it, etc. GWT.create then returns an instance of the generated class to its caller. Google calls this whole process Deferred Binding using Generators.

For more details on the GWT compiler, watch Ray Cromwell’s Optimizing apps with the GWT Compiler session at Google I/O.

Event bus changes

GWT is a special variant of Java and not everything from standard Java is available (primarily because GWT only supports what can be emulated in JavaScript). I mentioned before that reflection and dynamic proxies are not available, here are a few more differences you’ll want to note when comparing the code here with the pure Java event bus:

  1. JavaScript is single-threaded so we don’t have to worry about threading or synchronization for the most part.
  2. No threading also means no executor service, but we’ll use deferred (or scheduled) commands to simulate it.
  3. Thread-locals are also out.
  4. While JavaScript uses a garbage collector, we don’t have the same hooks into it as we do in Java. This means no soft references and no automatic releasing of observers.

EventBus.getPublisher() changes

The old, pure Java getPublisher method would implement the listener interface using a dynamic proxy. The proxy would publish events to the bus whenever called. This new GWT event bus keeps a registry of factories to create publishers that implement each type of event listener interface. Publishers also extend the EventPublisher class which allows getPublisher to initialize them with the event source, topic, etc. Like the non-GWT version, publishers queue events to the bus when called.

EventBus.publishEvent() changes

The only change to publishEvent is in how it schedules event delivery. The new method uses GWT’s scheduler service (formerly called deferred commands) since executor services are not supported.

EventBus.deliverEvent() changes

Changes to deliverEvent are also minimal. The cleanupGarbage() call is removed since we don’t have soft references to clean up and currentEvent is demoted from a thread-local to just an instance field.

Generated code

Let’s take a look at the code we need to generate. Here we produce two kinds of classes: a event publisher for each event listener and one PublisherFactoryRegistry implementation to hold the publisher factories.

Event publishers

Publishers implement the event listener interfaces, convert method calls into events, and publishes the events to the bus.

The code generator seeing the above SearchListener interface will create the following SearchListener__publisher class. Each implemented method creates an event object containing things you’d expect like event source, topic, and arguments. Since we don’t have reflection, the event object also contains an EventDispatcher to invoke the observer’s method when directed to do so by the bus.

Publisher factory registry

The PublisherFactoryRegistry class is used by the bus to find factories that create publishers (like SearchListener__publisher). The concrete PublisherFactoryRegistryImpl we generate is only there to register a factory for each publisher we generate — by putting it into the publishers HashMap.

Preserving the generated code

You can tell the GWT compiler to keep the generated Java code around for you to review. Just add the -gen folder compiler flag, where folder is the path to generate temporary files.

The Code Generator

The contract for GWT code generators is to extend com.google.gwt.core.ext.Generator and implement its generate(TreeLogger logger, GeneratorContext context, String typeName) method. The implemented method should return the name of the generated subclass for the type passed into GWT.create. As you review the code, you’ll notice that we’ve added several helper classes (ClassDeclaration, ClassBuilder, CSV, etc.) to make the code less verbose.

We’ve created an init method to act as a poor man’s constructor for the generator. It uses the compiler’s metadata registry (TypeOracle) to lookup several classes we’ll need later.

The no-arg generate method is where the work starts. Here we create the PublisherFactoryRegistryImpl class and give it an instance initializer where the factories are registered. We also call createPublisher() to generate the code for the publisher implementation for each EventListener sub-interface. The visitor pattern is used here to let us focus on code generation while delegating the EventListener finder logic to visitEventListeners().

The visitEventListeners method looks for types the compiler knows about that match the following criteria:

  1. are interfaces
  2. are not parameterized (like a java.util.List)
  3. extend java.util.EventListener
  4. have only void returning methods

The visitor’s visitClass method is called for matches, while partial matches emit a compiler warning along with the reason for the mismatch.

This lengthy method creates the publisher implementation. It implements each method in the event listener interface to publish its description and payload as an event to the bus.

Example

The downlad for this blog includes a simple search example to demonstrate using the event bus. You can run the EventBusExample module (/src/com/northconcepts/eventbusexample/EventBusExample.gwt.xml) or try out the precompiled app by browsing to WebContent/index.html.

Search App Mockup

The app has four display areas that plug into the event bus:

  1. SearchUI– accepts user input and starts the search
  2. ResultsUI – holds search results and handles clickfor full details
  3. DetailsUI– shows full details when result is clicked
  4. StatusUI – shows search progress and results summary.

The actual search function is performed by a non-visual component called SearchEngine. Like the display areas, it also plugs into the bus to both observer and publish events.

Event Bus - New Search event

When the user fills in the text field and presses the search button, the SearchUI publishes a newSearch event to the bus. The SearchEngine receives the notification and runs the search. The StatusUI also receives the notification and displays a search-in-progress message to the user.

Event Bus - Search finished event

When the search is complete, SearchEngine publishes a searchFinished message to the bus. The ResultsUI receives the notification and displays the data, if any, in a table to the user. The StatusUI also receives the notification and displays to the user either a message with the number of results found or a message that none were found.

Event Bus - Details viewed event

If results were found, the user can click one of the items in the ResultsUI which will publish a detailsViewed event. The DetailsUI will receive this event and display all the data for the selected record.

Conclusion

While GWT doesn’t support Java reflection, it does provide a powerful code generation facility. Code generation has the benefit of enabling dynamic, reflexive programming without the runtime cost. Once you get used to GWT’s approach, you’ll start seeing many opportunities to reduce your code size by leveraging GWT’s generators.

Download

The event bus download contains the entire source code (including Eclipse project). 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

We make Data Pipeline — a lightweight ETL framework for Java. Use it to filter, transform, and aggregate data on-the-fly in your web, mobile, and desktop apps. Learn more about it at northconcepts.com.

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=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">