Coder Social home page Coder Social logo

gwt-supereventbus's Introduction

What is SuperEventBus?

SuperEventBus is a high-powered event bus for GWT. It is a replacement for the built-in com.google.web.bindery.event.shared.EventBus.

SuperEventBus is currently an experimental API that is subject to change. Please leave feedback and feature requests in the issue tracker!

Why do I need a new event bus?

GWT's event bus is a great way to make your application more maintainable by decoupling components from one another, allowing you to work on one without affecting the others. However, the built-in event bus has several limitations. Specifically,

  • It's inflexible. All events in GWT's event system must extend an Event class, requiring you to define a new event for any kind of data that must be posted on the event bus. Like Guava's event bus, SuperEventBus allows you to post any type of event, allowing you to do things like update model objects by posting them directly on the bus. Handlers in GWT's event bus are also always active when registered - SuperEventBus allows you to define filters that can prevent events from being handled in certain situations (say, when the component is not visible.)
  • It's verbose. Extending Event requires implementing several boilerplate methods, and registering handlers usually requires creating clunky anonymous classes. SuperEventBus again takes inspiration from Guava, allowing you to annotate existing methods with the @Subscribe annotation to cause them to be automatically registered as event handlers.
  • It's monomorphic. A handler registered for a given event type will only hear instances of that exact type and none of its subtypes. This requires you to register redundant handlers for events that should be handled in similar ways. In contrast, SuperEventBus's handlers a fully polymorphic, so a handler for a given type will be invoked whenever any subtype of that event is posted. This lets you do powerful things, like defining tagging interfaces for events that share properties that should be handled in the same way, or even registering a handler for Object that will receive every event in the system. When just a single polymorphic type still isn't enough, SuperEventBus also allows the use of MultiEvents to handle many events with unrelated types using a single handler method.
  • It's unpredictable. GWT's event bus dispatches events using a stack, so that when one event fires another event it can be impossible to tell which order other handlers will see those events in. There is also no way to tell GWT that certain handlers should always be invoked before or after other handlers. SuperEventBus uses a queue that guarantees events will always be handled in the same order in which they're fired, and allows you to specify priorities for handlers to affect the order in which they are invoked.

SuperEventBus addresses all of these problems and more, providing a more powerful event bus that is easier to use.

How do I use SuperEventBus?

SuperEventBus uses a simple annotation-based interface inspired by Guava's event bus. To define a handler method, just annotate a non-private method that takes a single argument with the @Subscribe annotation like this:

@Subscribe
void handleString(String event) {
  Window.alert("The string " + event + " was posted on the event bus.");
}

A class may contain any number of methods annotated with @Subscribe. In order to active them, the class must first declare an event registration interface for the class:

interface MyRegistration extends EventRegistration<MyClass> {}

Then, the class just needs to register itself with an instance of the event bus (this is usually done in the constructor of the class):

@Inject
public MyClass(MyRegistration registration) {
  eventBus.register(this, registration);
}

Note that if you're not using Gin, you can also create MyRegistration directly via GWT.create instead of injecting it. Once registered, handleString will be invoked whenever a String is posted on the event bus, which is done like this:

eventBus.post("some string");

Of course, you aren't restricted to posting strings: handler methods can be defined for any type and any type can be passed to post. In practice, most users will define their own event types rather than posting existing types directly.

What else can it do?

SuperEventBus contains several advanced features that have no analogue in GWT's built-in event bus. It's easy to get started with SuperEventBus without these features, and explore them later as needed.

Priorities

In addition to @Subscribe, handler methods can also be annotated with the @WithPriority annotation, which takes a single integer. Handlers with higher priorities are always invoked before handlers with lower priorities. Handlers without the @WithPriority annotation are given priority 0, and negative priorities are allowed. See the javadoc for more details.

Filters

Handler methods can also be annotated with the @When annotation, which takes a filter class and causes that handler to be ignored when the filter returns false. Filter classes extend EventFilter and look like this:

class IsVisible implements EventFilter<HasVisibility, Object> {
  @Override
  public boolean accepts(HasVisibility handler, Object event) {
    return handler.isVisible();
  }
}

A handler annotated with @When(IsVisible.class) would be invoked only if its containing class was visible at the time the event was posted. Note that the filter accepts both the handler class as well as the event, so it is possible to filter based on the properties of either. See the javadoc for more details.

MultiEvents

Since EventBus is polymorphic, it is usually possible to handle many types of events by defining a handler for a common base class of those events. However, sometimes it is necessary to handle multiple events of unrelated types. This can be accomplished by declaring a handler with a paramter of type MultiEvent and annotating it with EventTypes as follows:

@Subscribe
void handleMultipleTypes(@EventTypes({String.class, Double.class}) MultiEvent event) {
  if (event instanceof String) {
    Window.alert("Got a string: " + event.getEvent());
  } else if (event instanceof Double) {
    Window.alert("Got a double: " + event.getEvent());
  }
}

The given handler would be invoked whenever a String or Double was posted on the event bus, and the actual event would be accessible via the getEvent method on MultiEvent. See the javadoc for more details.

Dead events

If an event is fired that has no registered handlers, SuperEventBus will wrap that event in a DeadEvent and re-fire it. This makes it possible to register a handler for DeadEvent that can do something like log a warning when an event without a handler is fired, which can help detect misconfiguration issues. Note that DeadEvent will never be fired if a handler for Object is registered, since that handler will receive every event posted on the event bus. See the javadoc for more details.

How do I install it?

If you're using Maven, you can add the following to your <dependencies> section:

<dependency>
  <groupId>com.ekuefler.supereventbus</groupId>
  <artifactId>supereventbus</artifactId>
  <version>0.1.1</version>
</dependency>

You can also download the jar directly or check out the source using git from https://github.com/ekuefler/gwt-supereventbus.git.

gwt-supereventbus's People

Contributors

arnor2000 avatar ekuefler avatar gallafent avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

gwt-supereventbus's Issues

Exception handling

Hi,

currently evaluating your EventBus implementation to see if I would prefer it to gwteventbinder and currently I am wondering if there is any reason why you provide a separate exception handling mechanism for the event bus?

You could have also used GWT's UmbrellaException to record exceptions occurred during event processing and then post that single UmbrellaException to GWT.getUncaughtExceptionHandler() if that handler has been set. During DevMode that handler is automatically set and logs to console.

Personally I believe that an event should never cause any exception and if it does it is totally unexpected. Well and for all unexpected exceptions I have an UncaughtExceptionHandler in place anyways that, when in production mode, sends them to the server along with additional information that help reproduce/debug the issue afterwards.

I can only hardly see any benefit in adding exception handlers to the event bus and in turn being forced to write more code just to be able to redirect these exceptions to the app's UncaughtExceptionHandler.

So any reason why the EventBus should have its own exception handling mechanism?

Migrate to annotation processors

Since GWT code generation will most likely be nuked with GWT 3.0, are there any plans to migrate the code generation part to annotation processors?

Could supereverntbus be more super in future?

we need to do

interface MyRegistration extends EventRegistration {}

and

eventBus.register(this, (MyRegistration) GWT.create(MyRegistration.class));

before using the @subscribe. That is boilerplate code like in gwteventbinder the Event need to extend GenericEvent.

I am also using guava EventBus with Spring in server side which let spring auto register

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class EventBusPostProcessor implements BeanPostProcessor
{

@Autowired
private EventBus eventBus;

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
{
    return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
    // for each method in the bean
    Method[] methods = bean.getClass().getMethods();

    for (Method method : methods)
    {
        // check the annotations on that method
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations)
        {
            // if it contains the Subscribe annotation
            if (annotation.annotationType().equals(Subscribe.class))
            {
                // register it with the event bus
                eventBus.register(bean);
                // we only need to register once
                return bean;
            }
        }
    }

    return bean;
}

}

Do you know any approach to do this ?
for example, with GIN ?

Thank you very much~!

Supereventbus doesn't bind classes in parent modules

I decided to create some common views / presenters in a module that I planned to reuse in various projects. I used supereventbus to bind some interactions; in debug mode, I can see that while the events are properly posted, they are never picked up by methods in the parent module that subscribe to them.

The presenters are initialized by gin in the child modules; perhaps that's preventing them from being properly scanned by supereventbus? I can share code with you that demonstrates this.

Sources don't compile, did I forget to inherit a required module?

I wanted to try this super EventBus which looks really great but I came across a compilation error at the very beginning.

I simply inherited from com.ekuefler.supereventbus.SuperEventBus and followed the guide to post an event between two classes, nice to use!

The problem is event is never send, obviously because EventRegistrationGenerator and EventRegistrationWriter don't compile. Compiler seems to complain about almost every classes they use.

Did I forget to inherit a module?

I'm using eclipse with GPE, GWT 2.5.1 and GAE 1.8.1.1.

Compiling...
      Compilation completed in 0,00 seconds
   Added 2719 units to cache since last cleanup.
   Validating units:
      Errors in 'com/ekuefler/supereventbus/rebind/EventRegistrationGenerator.java'
         Line 40: No source code is available for type com.google.gwt.core.ext.Generator; did you forget to inherit a required module?
         Line 43: No source code is available for type com.google.gwt.core.ext.TreeLogger; did you forget to inherit a required module?
         Line 43: No source code is available for type com.google.gwt.core.ext.GeneratorContext; did you forget to inherit a required module?
         Line 44: No source code is available for type com.google.gwt.core.ext.UnableToCompleteException; did you forget to inherit a required module?
         Line 46: No source code is available for type com.google.gwt.core.ext.typeinfo.JClassType; did you forget to inherit a required module?
         Line 48: No source code is available for type com.google.gwt.user.rebind.SourceWriter; did you forget to inherit a required module?
         Line 58: No source code is available for type com.google.gwt.core.ext.typeinfo.NotFoundException; did you forget to inherit a required module?
         Line 59: No source code is available for type com.google.gwt.core.ext.TreeLogger.Type; did you forget to inherit a required module?
         Line 64: No source code is available for type com.google.gwt.core.ext.typeinfo.TypeOracle; did you forget to inherit a required module?
         Line 73: No source code is available for type com.google.gwt.user.rebind.ClassSourceFileComposerFactory; did you forget to inherit a required module?
         Line 77: The method getCanonicalName() is undefined for the type Class<EventHandlerMethod>
         Line 78: The method getCanonicalName() is undefined for the type Class<LinkedList>
         Line 79: The method getCanonicalName() is undefined for the type Class<List>
         Line 80: The method getCanonicalName() is undefined for the type Class<MultiEvent>
         Line 82: No source code is available for type java.io.PrintWriter; did you forget to inherit a required module?
      Errors in 'com/ekuefler/supereventbus/rebind/EventRegistrationWriter.java'
         Line 44: No source code is available for type com.google.gwt.core.ext.TreeLogger; did you forget to inherit a required module?
         Line 53: No source code is available for type com.google.gwt.core.ext.typeinfo.JClassType; did you forget to inherit a required module?
         Line 53: No source code is available for type com.google.gwt.user.rebind.SourceWriter; did you forget to inherit a required module?
         Line 53: No source code is available for type com.google.gwt.core.ext.UnableToCompleteException; did you forget to inherit a required module?
         Line 60: The method format(String, String) is undefined for the type String
         Line 63: No source code is available for type com.google.gwt.core.ext.typeinfo.JMethod; did you forget to inherit a required module?
         Line 74: The method getCanonicalName() is undefined for the type Class<MultiEvent>
         Line 77: The method getCanonicalName() is undefined for the type Class<capture#1-of ?>
         Line 91: The method format(String, String) is undefined for the type String
         Line 127: No source code is available for type com.google.gwt.core.ext.TreeLogger.Type; did you forget to inherit a required module?
         Line 128: The method format(String, String, String) is undefined for the type String
         Line 133: The method format(String, String, String) is undefined for the type String
         Line 139: The method getCanonicalName() is undefined for the type Class<MultiEvent>
         Line 143: The method format(String, String, String) is undefined for the type String
         Line 152: The method isAssignableFrom(Class<capture#5-of ?>) is undefined for the type Class<capture#4-of ?>
         Line 155: The method getSimpleName() is undefined for the type Class<capture#6-of ?>
         Line 155: The method getSimpleName() is undefined for the type Class<capture#7-of ?>
         Line 164: The method format(String, String, String) is undefined for the type String
         Line 182: The method getSimpleName() is undefined for the type Class<capture#9-of ?>
         Line 190: The method getCanonicalName() is undefined for the type Class<capture#10-of ?>
         Line 198: No source code is available for type com.google.gwt.core.ext.typeinfo.JType; did you forget to inherit a required module?
         Line 200: No source code is available for type com.google.gwt.core.ext.typeinfo.JPrimitiveType; did you forget to inherit a required module?
         Line 225: No source code is available for type java.lang.reflect.Constructor<T>; did you forget to inherit a required module?
         Line 225: The method getConstructors() is undefined for the type Class<capture#11-of ?>
         Line 231: No source code is available for type java.lang.SecurityException; did you forget to inherit a required module?
   Removing invalidated units

EventRegistration should include methods from superclasses

Right now EventRegistration#getMethods only observes subscribed methods in the same class. It would be nice if it walked up the superclass hierarchy and added any annotated methods from ancestor classes as well.

Use case: I have several views that all share the same layout. I want to encapsulate the behavior of the layout (which has some events it should react to) in a superclass, and have the view classes entirely focused.

Subtyping doesn't work with overlay

I don't think it's a SuperEventBus bug, rather an overlay limitation but it should be at least documented.

Overlays are viewed only as JavaScriptObject and never as defined subtypes, causing all posted JSO subtypes events to be sent to all JSO subtypes subscribers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.