• Home
  • About
  • GWT Dependency Injection recipes using GIN (II)

    This is the second part of a series about Dependency Injection in Google Web Toolkit using GIN. If you have not yet read the first part, there we explained how to integrate GIN in an existing GWT sample application. In this second part, we will continue enhancing the sample application while explaining other types of injection supported by GIN.

    Encapsulating the event bus

    In the first part of this series, we configured the GWT event bus used in the original application to be injected in some of the application elements. Using an event bus is a “best practice” that helps communicating the different components in the application while keeping a low coupling between them. The point here was to show how to declare and configure a first dependency using the “injector” and the “module”. Because the class “SimpleEventBus” is provided by GWT, it was not possible to annotate the code of the class with “@Singleton” to set the “injection scope”. Instead, we use the Java DSL provided by GIN.
    In order to see how to do the same with annotations, let’s use now an application class (instead of a framework class) where we can use the annotations.
    As you probably noticed, we are using the event bus to emulate the “ping-pong” strokes. Instead of such a technical artifact, let’s create a “racket”:

    
    @Singleton
    public class Racket {
        private final EventBus fEventBus;
    
        private GwtEvent<?> fLastEvent;
    
        @Inject
        public Racket(EventBus eventBus) {
            fEventBus = eventBus;
        }
    
        public void serve() {
            fLastEvent = null;
            hit();
        }
    
        public void hit() {
            fEventBus.fireEvent(getNextEvent());
        }
    
        private GwtEvent<?> getNextEvent() {
            if (fLastEvent == null || fLastEvent instanceof PongEvent) {
                fLastEvent = new PingEvent();
            } else {
                fLastEvent = new PongEvent();
            }
            return fLastEvent;
        }
    
        public void onStroke(Class<?> pingPongEvent, final Command command) {
            if (pingPongEvent == PingEvent.class) {
                fEventBus.addHandler(PingEvent.TYPE, new PingEventHandler() {
                    public void onEvent(PingEvent event) {
                        command.execute();
                    }
                });
            } else {
                fEventBus.addHandler(PongEvent.TYPE, new PongEventHandler() {
                    public void onEvent(PongEvent event) {
                        command.execute();
                    }
                });
            }
        }
    }
    

    As you can see at the code, our class is annotated with “@Singleton” to indicate the injection scope and “@Inject” to get the event bus injected in the constructor. The class is a more adequate wrapper for the event handling and offers three methods: “serve”, “hit()” and “onStroke()”.
    To configure the injection please adjust the “injector” class like this:
    
    @GinModules(InjectorModule.class)
    public interface Injector extends Ginjector {
        public static final Injector INSTANCE = GWT.create(Injector.class);
    
        public Racket getRacket();
    
        public PingView getPingView();
    
        public PongView getPongView();
    }
    

    This time, we only need to declare the dependency in the “Injector” interface (the method will be invoked from the “Simulator” class) but no additional configuration is required and therefore no changes should be made in the “InjectorModule” class.
    Because now the event bus will not be directly accessed from the “Simulator” class, we have removed it from the “Injector” interface. But, let’s see the rest of the code changes:
    
    public class PingView extends Label {
        @Inject
        public PingView(final Racket racket) {
            racket.onStroke(PongEvent.class, new Command() {
                public void execute() {
                    setText("Pong");
    
                    new Timer() {
                        public void run() {
                            setText("");
                            racket.hit();
                        }
                    }.schedule(1000);
                }
            });
        }
    }
    

    The “PongView” class is almost identical and will not be shown here.
    
    public class Simulator implements EntryPoint {
        public void onModuleLoad() {
            final Injector injector = Injector.INSTANCE;
            RootPanel.get("pingSlot").add(injector.getPingView());
            RootPanel.get("pongSlot").add(injector.getPongView());
    
            final Button button = new Button("Serve!");
            button.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    button.setVisible(false);
                    injector.getRacket().serve();
                }
            });
            RootPanel.get("buttonSlot").add(button);
        }
    }
    

    Recapitulating what we have seen so far:

    • GIN allows to define “injectors” by mean of interfaces (“Injector” interface) that can be configured using one or more “modules” each (“InjectorModule” class).
    • If we need to configure an injection aspect we can use annotations in the class code or the Java DSL in the module.
    • In at least one point of the application we will need to access the “injector” to retrieve a top-level injected object (ex: “Racket”, “PingView”, “PongView” ). For these objects to be accessible through the injector, we need to declare them in the “injector” interface.
    • Any other dependency that is declared to be injected (annotated with @Inject) will be resolved by GIN using the algorithm described in the first part of this series and does not need to be declared in the “injector” interface (ex: “EventBus”).

    If you are familiar with GUICE, you probably already know other ways of configuring dependencies and injection. Let’s see in the next point how to avoid the repetition in the views by using “assisted injection”.

    Using assisted injection to avoid code repetition

    One of the main applications of dependency injection is to avoid coding factory classes and singletons and also centralizing the application’s configuration values. This is a very powerful tool to configure application-wide objects like services, which typically are either “singletons” or “prototypes” and scarce.

    When working with data, persistency tools like JPA assist us with the task of generating Java objects for our numerous domain model entities. These objects have normally no behavior or just a little which is typically coded in the “entity” classes. But, what happens if we want to inject our services in some of these entities to have a richer domain model? Normally, we have to tackle this task ourselves and inject the services by hand. This happens because such objects do not by default participate in the injection context (unless we are using JEE or weaving).

    While this not a difficult task, it is a purely programmatic one and breaks one of the principles of dependency injection: declarative dependencies definition.
    In GUICE (and therefore in GIN) there is a concept called “assisted injection” that helps us to create objects which require dependencies from the injection context but also instance specific property values. Basically, it consists of using a factory interface with methods to instantiate the objects as managed beans (injected objects) receiving each method only the object’s specific properties as parameters. The object constructors receive of course not only the object properties but also the dependencies who should be retrieved from the dependency injection context.

    Because an image is worth a thousand words, let’s see this with an example. What we would like to achieve here is to have only one view class (“PingPongView”) instead of two specific ones that have almost the same code. If we inspect both view classes, the differences are: to which event each reacts and which text each shows. If we refactor these values as constructor parameters, we have the following resulting class:

    
    public class PingPongView extends Label {
        @Inject
        public PingPongView(final Racket racket, @Assisted final String text, @Assisted Class<? extends GwtEvent> eventClass) {
            racket.onStroke(eventClass, new Command() {
                public void execute() {
                    setText(text);
    
                    new Timer() {
                        public void run() {
                            setText("");
                            racket.hit();
                        }
                    }.schedule(1000);
                }
            });
        }
    }
    

    Probably you already noticed the @Assisted annotation in the new extracted parameters. It just indicates GIN that the non-annotated parameters should be resolved as dependencies and that the annotated ones should be provided though parameters in a factory method of the assisted injection factory:
    
    public interface AssistedInjectionFactory {
        public PingPongView createPingPongView(String text, Class<? extends GwtEvent> eventClass);
    }
    

    To install this factory in our dependency injection context we need to modify our GIN module class like this:
    
    public class InjectorModule extends AbstractGinModule {
        @Override
        protected void configure() {
            bind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class);
    
            install(new GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));
        }
    }
    

    And in the injector class, we have to expose the factory as any other dependency:
    
    @GinModules(InjectorModule.class)
    public interface Injector extends Ginjector {
        public static final Injector INSTANCE = GWT.create(Injector.class);
    
        public Racket getRacket();
        public AssistedInjectionFactory getFactory();
    }
    

    And last but not least, let’s use the assisted injection in our application:
    
    ...
        public void onModuleLoad() {
            final Injector injector = Injector.INSTANCE;
            final AssistedInjectionFactory factory = injector.getFactory();
            RootPanel.get("pingSlot").add(factory.createPingPongView("Ping", PingEvent.class));
            RootPanel.get("pongSlot").add(factory.createPingPongView("Pong", PongEvent.class));
    ...
    

    In this article, we have applied new dependency injection recipes to our GWT demo application. I hope that they can help you give a better structure to your GWT applications and also learn dependency injection features and its “best practices”.

    The source code of the application can be downloaded here. To see the application working, unzip the file, change to the folder where the Maven pom file is stored and type the command: “mvn clean gwt:run”. After the GWT “Development mode” application starts, click on the “Launch Default Browser” button.

    The other parts of this series can be found here and here.

    I hope that you enjoyed reading this article as much as I did writing it.
    See you soon!

    Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
    • Y!GG
    • Webnews
    • Digg
    • del.icio.us
    • DotNetKicks
    • Facebook
    • Google Bookmarks
    • Newsrider
    • Newstube
    • TwitThis
    • YahooBuzz

    Comments are closed.