Dependency Injection in Xtext with Google Guice

All Xtext components are assembled by means of Dependency Injection (DI). This means basically that whenever some code is in need for functionality (or state) from another component, one just declares the dependency rather then stating how to resolve it, i.e. obtaining that component.

For instance when some code wants to use a scope provider, it just declares a field (or method or constructor) and adds the @Inject annotation:

public class MyLanguageLinker extends Linker {

  @Inject
  private IScopeProvider scopeProvider;

}
 

It is not the duty of the code to care about where the IScopeProvider comes from or how it is created. When above’s class is instantiated, Guice sees that it requires an instance of IScopeProvider and assigns it to the specified field or method parameter. This of course only works, if the object itself is created by Guice. In Xtext almost every instance is created that way and therefore the whole dependency net is controlled and configured by the means of Google Guice.

Guice of course needs to know how to instantiate real objects for declared dependencies. This is done in so called Modules. A Module defines a set of mappings from types to either existing instances, instance providers or concrete classes. Modules are implemented in Java. Here’s an example:

public class MyDslRuntimeModule extends AbstractMyDslRuntimeModule {
@Override
public void configure(Binder binder) {
super.configure(binder);
binder.bind(IScopeProvider.class).to(MyConcreteScopeProvider.class);
}
}

With plain Guice modules one implements a method called configure and gets a so called Binder passed in. That binder provides a fluent API to define the mentioned mappings. This was just a very brief and simplified description. We highly recommend to have a look at the website Google Guice to learn more.

The Module API

Xtext comes with a slightly enhanced module API. For your language you get two different modules: One for the runtime bundle which is used when executing your language infrastructure outside of Eclipse such as on the build server. The other is located in the UI bundle and adds or overrides bindings when Xtext is used within an Eclipse environment.

The enhancement we added to Guice’s Module API is that we provide an abstract base class, which reflectively looks for certain methods in order to find declared bindings. The most common kind of method is :

public Class<? extends IScopeProvider> bindIScopeProvider() {
return MyConcreteScopeProvider.class;
}

which would do the same as the code snippet above. It simply declares a binding from IScopeProvider to MyConcreteScopeProvider. That binding will make Guice instantiate and inject a new instance of MyConcreteScopeProvider whenever a dependency to IScopeProvider is declared.

Having a method per binding allows to deactivate individual bindings by overriding the corresponding methods and either change the binding by returning a different target type or removing that binding completely by returning null.

There are two additional kinds of binding-methods supported. The first one allows to configure a provider. A Provider is an interface with just one method :

public interface Provider<T> {

  /**
   * Provides an instance of {@code T}. Must never return {@code null}.
   */
  T get();
}

This one can be used if you need a hook whenever an instance of a certain type is created. For instance if you want to provide lazy access to a singleton or you need to do some computation each time an instance is created (i.e. factory). If you want to point to a provider rather than to a concrete class you can use the following binding method.

public Class<? extends Provider<IScopeProvider>> provideIScopeProvider() {
return MyConcreteScopeProviderFactory.class;
}

(Please forgive us the overuse of the term provider. The IScopeProvider is not a Guice provider .)

That binding tells Guice to instantiate MyConcreteScopeProviderFactory and invoke get() in order to obtain an instance of IScopeProvider for clients having declared a dependency to that type. Both mentioned methods are allowed to return an instance instead of a type. This may be useful if some global state should be shared in the application:

public Provider<IScopeProvider> provideIScopeProvider() {
return new MyConcreteScopeProviderFactory();
}

or

public IScopeProvider bindIScopeProvider() {
return new MyConcreteScopeProvider();
}

respectively.

The last binding method provided by Xtext allows to do anything you can do with Guice’s binding API, since it allows you to use it directly. If your method’s name starts with the name ‘configure’, has a return type void and accepts one argument of type Binder

public void configureIScopeProvider(Binder binder) {
binder.bind(IScopeProvider.class).to(MyConcreteScopeProvider.class);


Obtaining an Injector

In every application wired up with Guice there is usually one point where you initialize a so called Injector using the modules declared and after that using that injector to create the root instance of the whole application. In plain Java environments this is something that’s done in the main method. It could look like this:

public static void main(String[] args) {
Injector injector = Guice.createInjector(new MyDslRuntimeModule());
MyApplication application = injector.getInstance(MyApplication.class);
application.run();
}

Xtext uses EMF which makes use of a couple of global registries, which have to be configured on startup. Because we of course want to leverage Guice also for all factories, etc. that we put into those registries, we have introduced a so called ISetup which provides a method called Injector createInjectorAndDoEMFRegistration(). So instead of using the plain Guice code shown above you rather use the ISetup class generated for your language, which, as the method name suggests, creates an Injector and uses it to initialize a couple of EMF objects and register them in the corresponding registries.

Injector injector = 
    new MyStandaloneSetup().createInjectorAndDoEMFRegistration(); 

These are the basic ideas around Guice and the small extension Xtext provides on top. For more information we strongly encourage you to read through the documentation on the website of Google Guice.