My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
GinTutorial  
Using GIN to create a GWT widget
Featured
Updated Aug 23, 2011 by aragos

This tutorial will show how a simple GWT widget can be constructed with GIN. You should be already familiar with Guice - if not, then please read the Guice User Guide for an introductory overview of Guice concepts.

Introduction

In regular Guice, you would use:

// Does NOT work in GWT code!
MyWidgetMainPanel mainPanel = injector.getInstance(MyWidgetMainPanel.class);

However, as the comment points out that won't work in GWT:

  • There is no actual class available in the resulting JavaScript.
  • Guice makes heavy use of reflection, most of which GWT does not emulate.

Fortunately in GWT there is a different idiom which accomplishes the same purpose.

Using Gin

Step 1. Inheriting the GIN module

<module>
  ...
  <inherits name="com.google.gwt.inject.Inject"/>
  ...
</module>

Step 2. Defining the Ginjector

Declare an interface with methods that return the desired types:

public interface MyWidgetGinjector extends Ginjector {
  MyWidgetMainPanel getMainPanel();
}

Experienced GWT users will notice the similarity to the way GWT image bundles and messages are done: You simply create a method for each object type you want to create, and the an implementation of the interface gets generated for you at compile time.

Note that you only need to create injector methods for classes that you would directly access in your top-level initialization code, such as the UI classes to install in your RootPanel. You don't need to create injector methods for lower-level classes that will be automatically injected. So for example, if Class A uses class B which uses class C, you only need to create an injector method for A, as the other classes B and C will automatically be injected into A.

In other words, injector methods provide a bridge between the Guice and non-Guice world.

Step 3. Declare bindings

The next step is to bind the various classes and providers using a Guice module. The module class looks almost exactly like it would in regular Guice (We use the GinModule and AbstractGinModule instead of Module and AbstractModule.) Here's an example module:

public class MyWidgetClientModule extends AbstractGinModule {
  protected void configure() {
    bind(MyWidgetMainPanel.class).in(Singleton.class);
    bind(MyRemoteService.class).toProvider(MyRemoteServiceProvider.class);
  }
}

Note that if GIN can't find a binding for a class, it falls back to calling GWT.create() on that class. What this means that image bundles and translated messages will just magically work.

We emulate most of Guice's types but not modules, because:

  • We need to be able to develop GIN at our own pace.
  • We will probably never support toInstance(...) and the like because we do our work with modules at compile time, not runtime.

For compatibility with regular Guice we provide a GinModuleAdapter class, which makes your GinModule available as a Module.

Step 4. Associating the module with the injector

Add the GinModules annotation to your Ginjector, specifying the module(s) needed to configure the application.

@GinModules(MyWidgetClientModule.class)
public interface MyWidgetGinjector extends Ginjector {
  MyWidgetMainPanel getMainPanel();
}

Current project layout:

MyWidgetProject/
    client/
        MyWidget.java
        MyWidgetGinjector.java
        MyWidgetMainPanel.java
        MyWidgetClientModule.java
    public/
    server/

Step 5. Creating the Ginjector

To create the injector instance, use the standard GWT.create() call. This can be done during static initialization:

public class MyWidget implements EntryPoint {
  private final MyWidgetGinjector injector = GWT.create(MyWidgetGinjector.class);

  public void onModuleLoad() {
    MyWidgetMainPanel mainPanel = injector.getMainPanel();
    RootPanel.get().add(mainPanel);
  }

}

Compilation

Your project should now be ready to be compiled. Since Gin uses Guice at compile-time and Guice operates on compiled java classes, you'll have to provide the compiled java classes to the gwt compiler. Example from an ant build file:

<target name="javac" description="Compiles Java types needed during GWT compilation">
  <mkdir dir="war/WEB-INF/classes"/>
  <javac srcdir="src" destdir="war/WEB-INF/classes">
    <classpath refid="project.libs"/>
  </javac>
</target>

<target name="gwtc" depends="javac" description="GWT Web Compilation">
  <java classname="com.google.gwt.dev.Compiler">
    <classpath>
      <pathelement location="src"/>

       <!-- Note the reference to the compiled java classes -->
      <pathelement location="war/WEB-INF/classes"/>
      <path refid="project.libs"/>
    </classpath>
     <arg value="com.example.myapp.MyApp"/>
  </java>
</target>

Full Example

You can find an example for Gin project layout and compilation in our samples collection.

Gin "Magic"

Gin tries to make injection painless and remove as much boilerplate from your code as possible. To do that the generated code includes some magic behind the scenes which is explained here.

Deferred Binding

One way Gin optimizes code is by automating GWT deferred binding. So if you inject an interface or class1 bound through deferred binding (but not through a Guice/Gin binding), Gin will internally call GWT.create on it and inject the result. One example are GWT messages and constants (used for i18n purposes):

public interface MyConstants extends Constants {
  String myWords();
}

public class MyWidget {

  @Inject
  public MyWidget(MyConstants myconstants) {
    // The injected constants object will be fully initialized - 
    // GWT.create has been called on it and no further work is necessary.
  }
}

Note: Gin will not bind the instances created through GWT.create in singleton scope. That should not cause unnecessary overhead though, since deferred binding generators usually implement singleton patterns in their generated code.

1: Gin creates all instances through GWT.create (instead of new) for which it is legal: interfaces and classes with default constructors (whether they have an @Inject annotation or not).

Remote Services

The RemoteService magic Gin performs is an extension of the deferred binding optimization explained above. Every time Gin is asked to inject an asynchronous remote service, it will inject an instance retrieved through calling GWT.create on its regular remote service:

public interface MyRemoteService extends RemoteService { ... }

public interface MyRemoteServiceAsync { ... }

public class MyWidget {
  
  @Inject
  public MyWidget(MyRemoteServiceAsync service) {
    // The 'service' will be created by calling GWT.create(MyRemoteService.class)
    // prior to injection. 
  }
}
Comment by k...@bolay.de, Nov 11, 2008

Gin also performs some magic when dealing with async remote services (GWT-RPC) and actually calls GWT.create(MyRemoteSyncService.class) if the Ginjector defines a method returning MyRemoteSyncServiceAsync.

Comment by kristian...@gmail.com, Nov 12, 2010

Hi, I'm new to GIN but have a few questions 1. For the Remote Services example do you still need to define this in a module somewhere or is the point that you can just annotate with @Inject and it will work. 2. When using MVP, where is the best place to use dependency injection. Should I use it to create my presenters, so I can do injector.getPresenter(); 3. Is it possible to inject simple objects such as strings.

Comment by boppenh...@google.com, Nov 13, 2010

1. Nope, nothing to do, just put your remote service as a parameter on an @Inject constructor. 2. I've seen lots of debate on this issue. 3. Yes, use binding annotations. (http://code.google.com/p/google-gin/source/browse/trunk/samples/HigherLower/src/com/google/gwt/gin/higherlower/client/gin/HigherOrLowerModule.java)

Comment by SVR...@gmail.com, Apr 21, 2011

I am new to Gin and using GWT 2.2, I added all the dependencies including guice-assistedinject-snapshot.jar, but I get this error on compile:

Generator 'com.google.gwt.inject.rebind.GinjectorGenerator?' threw an exception while rebinding 'com.omnex.dashboard.prototype.gwt.widget.TxnTopLevelWidget?.OnePageWidgetGinjector?'
java? com.google.inject.internal.ComputationException?: com.google.inject.internal.ComputationException?: com.google.inject.internal.ComputationException?: java.lang.NoClassDefFoundError?: com.google.inject.assistedinject.Assisted java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:553) java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:419) java? at com.google.inject.internal.CustomConcurrentHashMap?$ComputingImpl?.get(CustomConcurrentHashMap?.java:2041) java? at com.google.inject.internal.FailableCache?.get(FailableCache?.java:46) java? at com.google.inject.internal.ConstructorInjectorStore?.get(ConstructorInjectorStore?.java:48) java? at com.google.inject.internal.ConstructorBindingImpl?.initialize(ConstructorBindingImpl?.java:121) java? at com.google.inject.internal.InjectorImpl?.initializeJitBinding(InjectorImpl?.java:443) java? at com.google.inject.internal.InjectorImpl?.createJustInTimeBinding(InjectorImpl?.java:698) java? at com.google.inject.internal.InjectorImpl?.createJustInTimeBindingRecursive(InjectorImpl?.java:630) java? at com.google.inject.internal.InjectorImpl?.getJustInTimeBinding(InjectorImpl?.java:223) java? at com.google.inject.internal.InjectorImpl?.getBindingOrThrow(InjectorImpl?.java:174) java? at com.google.inject.internal.InjectorImpl?.getInternalFactory(InjectorImpl?.java:704) java? at com.google.inject.internal.FactoryProxy?.notify(FactoryProxy?.java:45) java? at com.google.inject.internal.BindingProcessor?.runCreationListeners(BindingProcessor?.java:238) java? at com.google.inject.internal.InternalInjectorCreator?.initializeStatically(InternalInjectorCreator?.java:143) java? at com.google.inject.internal.InternalInjectorCreator?.build(InternalInjectorCreator?.java:116) java? at com.google.inject.InjectorBuilder?.build(InjectorBuilder?.java:101) java? at com.google.inject.Guice.createInjector(Guice.java:105) java? at com.google.inject.Guice.createInjector(Guice.java:90) java? at com.google.gwt.inject.rebind.GinjectorGenerator?.generate(GinjectorGenerator?.java:47) java? at com.google.gwt.core.ext.GeneratorExtWrapper?.generate(GeneratorExtWrapper?.java:48) java? at com.google.gwt.core.ext.GeneratorExtWrapper?.generateIncrementally(GeneratorExtWrapper?.java:60) java? at com.google.gwt.dev.javac.StandardGeneratorContext?.runGeneratorIncrementally(StandardGeneratorContext?.java:662) java? at com.google.gwt.dev.cfg.RuleGenerateWith?.realize(RuleGenerateWith?.java:41) java? at com.google.gwt.dev.shell.StandardRebindOracle?$Rebinder.rebind(StandardRebindOracle?.java:74) java? at com.google.gwt.dev.shell.StandardRebindOracle?.rebind(StandardRebindOracle?.java:259) java? at com.google.gwt.dev.shell.StandardRebindOracle?.rebind(StandardRebindOracle?.java:248) java? at com.google.gwt.dev.DistillerRebindPermutationOracle?.getAllPossibleRebindAnswers(DistillerRebindPermutationOracle?.java:91) java? at com.google.gwt.dev.jdt.WebModeCompilerFrontEnd?.doFindAdditionalTypesUsingRebinds(WebModeCompilerFrontEnd?.java:106) java? at com.google.gwt.dev.jdt.AbstractCompiler?$Sandbox$CompilerImpl?.process(AbstractCompiler?.java:254) java? at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:444) java? at com.google.gwt.dev.jdt.AbstractCompiler?$Sandbox$CompilerImpl?.compile(AbstractCompiler?.java:175) java? at com.google.gwt.dev.jdt.AbstractCompiler?$Sandbox$CompilerImpl?.compile(AbstractCompiler?.java:288) java? at com.google.gwt.dev.jdt.AbstractCompiler?$Sandbox$CompilerImpl?.access$400(AbstractCompiler?.java:145) java? at com.google.gwt.dev.jdt.AbstractCompiler?.compile(AbstractCompiler?.java:632) java? at com.google.gwt.dev.jdt.BasicWebModeCompiler?.getCompilationUnitDeclarations(BasicWebModeCompiler?.java:124) java? at com.google.gwt.dev.jdt.WebModeCompilerFrontEnd?.getCompilationUnitDeclarations(WebModeCompilerFrontEnd?.java:54) java? at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler?.precompile(JavaToJavaScriptCompiler?.java:517) java? at com.google.gwt.dev.jjs.JavaScriptCompiler?.precompile(JavaScriptCompiler?.java:35) java? at com.google.gwt.dev.Precompile.precompile(Precompile.java:541) java? at com.google.gwt.dev.Precompile.precompile(Precompile.java:495) java? at com.google.gwt.dev.Precompile.precompile(Precompile.java:407) java? at com.google.gwt.dev.Compiler.run(Compiler.java:215) java? at com.google.gwt.dev.Compiler.run(Compiler.java:187) java? at com.google.gwt.dev.Compiler$1.run(Compiler.java:159) java? at com.google.gwt.dev.CompileTaskRunner?.doRun(CompileTaskRunner?.java:87) java? at com.google.gwt.dev.CompileTaskRunner?.runWithAppropriateLogger(CompileTaskRunner?.java:81) java? at com.google.gwt.dev.Compiler.main(Compiler.java:166) java? Caused by: com.google.inject.internal.ComputationException?: com.google.inject.internal.ComputationException?: java.lang.NoClassDefFoundError?: com.google.inject.assistedinject.Assisted java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:553) java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:419) java? at com.google.inject.internal.CustomConcurrentHashMap?$ComputingImpl?.get(CustomConcurrentHashMap?.java:2041) java? at com.google.inject.internal.FailableCache?.get(FailableCache?.java:46) java? at com.google.inject.internal.ConstructorInjectorStore?.get(ConstructorInjectorStore?.java:48) java? at com.google.inject.internal.ConstructorBindingImpl?.initialize(ConstructorBindingImpl?.java:121) java? at com.google.inject.internal.InjectorImpl?.initializeJitBinding(InjectorImpl?.java:443) java? at com.google.inject.internal.InjectorImpl?.createJustInTimeBinding(InjectorImpl?.java:698) java? at com.google.inject.internal.InjectorImpl?.createJustInTimeBindingRecursive(InjectorImpl?.java:630) java? at com.google.inject.internal.InjectorImpl?.getJustInTimeBinding(InjectorImpl?.java:223) java? at com.google.inject.internal.InjectorImpl?.getBindingOrThrow(InjectorImpl?.java:174) java? at com.google.inject.internal.InjectorImpl?.createProviderBinding(InjectorImpl?.java:281) java? at com.google.inject.internal.InjectorImpl?.createJustInTimeBinding(InjectorImpl?.java:662) java? at com.google.inject.internal.InjectorImpl?.createJustInTimeBindingRecursive(InjectorImpl?.java:630) java? at com.google.inject.internal.InjectorImpl?.getJustInTimeBinding(InjectorImpl?.java:223) java? at com.google.inject.internal.InjectorImpl?.getBindingOrThrow(InjectorImpl?.java:174) java? at com.google.inject.internal.InjectorImpl?.getInternalFactory(InjectorImpl?.java:704) java? at com.google.inject.internal.InjectorImpl?.createParameterInjector(InjectorImpl?.java:772) java? at com.google.inject.internal.InjectorImpl?.getParametersInjectors(InjectorImpl?.java:760) java? at com.google.inject.internal.ConstructorInjectorStore?.createConstructor(ConstructorInjectorStore?.java:55) java? at com.google.inject.internal.ConstructorInjectorStore?.access$000(ConstructorInjectorStore?.java:27) java? at com.google.inject.internal.ConstructorInjectorStore?$1.create(ConstructorInjectorStore?.java:35) java? at com.google.inject.internal.ConstructorInjectorStore?$1.create(ConstructorInjectorStore?.java:33) java? at com.google.inject.internal.FailableCache?$1.apply(FailableCache?.java:35) java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:549) java? ... 47 more java? Caused by: com.google.inject.internal.ComputationException?: java.lang.NoClassDefFoundError?: com.google.inject.assistedinject.Assisted java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:553) java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:419) java? at com.google.inject.internal.CustomConcurrentHashMap?$ComputingImpl?.get(CustomConcurrentHashMap?.java:2041) java? at com.google.inject.internal.FailableCache?.get(FailableCache?.java:46) java? at com.google.inject.internal.MembersInjectorStore?.get(MembersInjectorStore?.java:63) java? at com.google.inject.internal.ConstructorInjectorStore?.createConstructor(ConstructorInjectorStore?.java:59) java? at com.google.inject.internal.ConstructorInjectorStore?.access$000(ConstructorInjectorStore?.java:27) java? at com.google.inject.internal.ConstructorInjectorStore?$1.create(ConstructorInjectorStore?.java:35) java? at com.google.inject.internal.ConstructorInjectorStore?$1.create(ConstructorInjectorStore?.java:33) java? at com.google.inject.internal.FailableCache?$1.apply(FailableCache?.java:35) java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:549) java? ... 71 more java? Caused by: java.lang.NoClassDefFoundError?: com.google.inject.assistedinject.Assisted java? at java.lang.Class.getDeclaredFieldsImpl(Native Method) java? at java.lang.Class.getDeclaredFields(Class.java:534) java? at com.google.inject.spi.InjectionPoint?.getInjectionPoints(InjectionPoint?.java:605) java? at com.google.inject.spi.InjectionPoint?.forInstanceMethodsAndFields(InjectionPoint?.java:345) java? at com.google.inject.internal.MembersInjectorStore?.createWithListeners(MembersInjectorStore?.java:75) java? at com.google.inject.internal.MembersInjectorStore?.access$000(MembersInjectorStore?.java:32) java? at com.google.inject.internal.MembersInjectorStore?$1.create(MembersInjectorStore?.java:40) java? at com.google.inject.internal.MembersInjectorStore?$1.create(MembersInjectorStore?.java:38) java? at com.google.inject.internal.FailableCache?$1.apply(FailableCache?.java:35) java? at com.google.inject.internal.MapMaker?$StrategyImpl?.compute(MapMaker?.java:549) java? ... 81 more java? Caused by: java.lang.ClassNotFoundException?: com.google.inject.assistedinject.Assisted java? at java.net.URLClassLoader.findClass(URLClassLoader.java:496) java? at java.lang.ClassLoader?.loadClass(ClassLoader?.java:631) java? at sun.misc.Launcher$AppClassLoader?.loadClass(Launcher.java:349) java? at java.lang.ClassLoader?.loadClass(ClassLoader?.java:597)
Comment by siyabong...@gmail.com, Sep 21, 2011

Just make sure you do not bind any type called "Cache". You will errors/exceptions, but it will just not work... silently!!

Comment by nick.heiner@gmail.com, Feb 10, 2012

SVR, I'm getting that same error.

Comment by saurabh....@gmail.com, Mar 5, 2012

I was going through the sample HigherLower? and encountered this line

// TODO we could probably use a generator instead of this nonsense.
in com.google.gwt.gin.higherlower.client.gin.DeckProvider?. May be a dumb question but I would really like to know how I can implement a generator here.

Comment by saurabh....@gmail.com, May 8, 2012

does gin provide autowiring by type?


Sign in to add a comment
Powered by Google Project Hosting