RSS

Search Engine

Saturday, October 23, 2010

Programming model with Dependency Injection and Annotations

Eclipse e4 introduces a new programming model based on annotations and dependency injection . This new programming approach allows to avoid the usage of Singletons and emphasis loosely coupled components. Using Eclipse Singletons should be avoided in an Eclipse based application because this couples the code tightly to a special implementation of the platform and make it difficult to test the application.

6.1. e4 runtime

We learned in the Eclipse e4 model chapter that the Eclipse workbench is described by a EMF model . This model contains references to Java classes. During startup the e4 runtime evaluates the model and instantiate these class if required. The life cycle of every model element is therefore controlled by the e4 runtime. The e4 runtime instantiated and destroys the Java objects which are referred to by model elements if required. The model elements can also contain annotations which describe their dependencies and / or which methods should be executed at which point in time. The e4 runtimes scans every objects for these annotations and based on the annotations it performs certain actions. For example the @inject annotation indicates that the e4 framework should inject other Java Objects or OSGi Services into the Java Object for the model element. Other annotations control which methods are called at certain points of the object life cycles, e.g. the @execute annotations describes which method should be called in a command handler is called.

6.2. Using Dependency Injection

In every model element you can automatically retrieve services and other objects via annotations if these services and objects are known to the Eclipse e4 runtime. For non model elements the programmer has to call a framework method to get the known Services may be pre-defined by the Eclipse platform or may be defined by the programmer. All objects which can be injected into a model element are stored in the so-called IEclipseContext. This Context can be extended via additional OSGi services or by own objects.

To describe dependencies the following annotations are available.

Table 1. Annotations for dependency injection

AnnotationDescription
@javax.inject.InjectMark a field/constructor/method to be injected by the DI-Framework
@javax.inject.NamedDefine the key for the value which should be injected. By default the fully qualified class name is used.
@org.eclipse.e4.core.di.annotations.OptionalMark an inject value to be optional so if it can’t be found no error is thrown. This also ensures that the value is unset if the inject value is set to null.
@org.eclipse.e4.core.di.extensions.PreferenceMarked field/method-parameter will be filled with a value from the preference store

6.3. IEclipseContext

For the purpose of dependency injection Eclipse provides the IEclipseContext. IEclipseContext is a mechanism to find for a service or object request the fitting one. The IEclipseContext is hierarchical structured and every model elements has its own context. If a service cannot be found in a local context then the parent context is searched. OSGi services are part of the application context which is the highest level is the hierarchy. The application context is the interface to the OSGi services.

The IEclipseContext contains two types of elements: workbench model objects and service. The IEclipseContext is hierarchical and every Model objects that are themselves MContext instances will have their own content. This content will also include a reference to the Model object itself.

Services and model compoenents can be injected into a Eclipse e4 model component via the @Inject annotation. As said the content is hierarchical and the Eclipse dependency injection framework will first try to find the object in the content closest to the object and then move up to the next level. If the service cannot be found in the context then the framework will try to find the object as OSGi service.

See Eclipse e4 services Wiki for an overview of the available services. The usage of these services is also described Available services for dependency injection .

6.4. Available services

Workbench Services follow usually with E*Service pattern. For example you have:

  • org.eclipse.e4.core.commands.ECommandService

  • org.eclipse.e4.core.commands.EHandlerService

  • org.eclipse.e4.workbench.modeling.EPartService

  • org.eclipse.e4.workbench.modeling.EModelService

  • org.eclipse.e4.workbench.modeling.ESelectionService

  • org.eclipse.e4.ui.bindings.EBindingService

  • org.eclipse.e4.ui.services.EContextService

Other available services are:

  • org.eclipse.e4.core.services.Adapter

  • org.eclipse.e4.core.services.StatusReporter

  • org.eclipse.e4.core.services.Logger

  • org.eclipse.e4.core.services.events.IEventBroker

  • org.eclipse.e4.workbench.ui.IPresentationEngine

  • org.eclipse.jface.window.IShellProvider

6.5. Annotations for application hooks

Eclipse e4 uses annotations to indicate that certain behavior is expected from the framework. These annotations can indicate that certain values should be injected in model parts (see later the description of dependency injection), or are used to indicate that methods should be called at certain events. The following annotations stear workbench behavior; we will later see the annotations for dependency injections. are most important.

Table 2. Eclipse e4 annotations with no relationship to DI

AnnotationDescription
@org.eclipse.e4.core.di.annotations.ExecuteMarks a method in a command to be executed
@org.eclipse.e4.core.di.annotations.CanExecuteMark a method to be visited by the Command- Framework to check if a command is enabled
@org.eclipse.e4.workbench.ui.Persistannotates method. If used in a Part-POJO this method will be called when the workbench issues a save-request

6.6. Example

The following shows a part (view). Via the @inject annotation it specifies that it requires object of type Logger, the IEcipseContext and a Composite. At runtime the e4 runtime will create an instance of this part. During this creation it checks of annotations and injects the required objects into the view if they are available.

    
package de.vogella.e4.rcp.first.parts;

import javax.inject.Inject;

import org.eclipse.e4.core.services.Logger;
import org.eclipse.e4.core.services.annotations.PostConstruct;
import org.eclipse.e4.core.services.context.IEclipseContext;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class View1 {

@Inject private Logger logger;
@Inject private IEclipseContext context;
@Inject private Composite parent;

// PostConstruct ensures that the dependency injection has been finished
// this this method is called
@PostConstruct
public void init() {
// If you don't inject the Logger you could alternatively
// get it via the IEclipseContext with the following statement
// Logger log= (Logger) context.get(Logger.class.getName());
System.out.println(context!=null);
System.out.println(logger!=null);
logger.info("UI will start to build");

Label label = new Label(parent, SWT.NONE);
label.setText("E4 is new");
Text text = new Text(parent, SWT.NONE);
text.setText("and different");
}

}

0 comments:

Post a Comment