Eclipse 4.0: Inject your OWN Objects

One of the great new features of Eclipse 4.0 is the use of dependency injection (DI). The new architecture does not use Singletons at all, but injects all the necessary objects in a given context. For example, a view is a POJO now and the necessary parent composite is injected:

public class ContactView implements IContactView {

	@Inject
	public ContactView(Composite parent) {
...

But how could you use DI to inject your own objects? It’s easy and straight forward: Just annotate the object (A) that needs your own object (B) injected with @Inject, either on attribute or on method level. The nice thing about this is that all available injectable objects (e.g. from the context) will automatically injected into (B). As an example, you could create a presenter that is not interested in UI toolkit (e.g. SWT) specific stuff but needs a view (that is interested in the SWT parent composite).

public class ContactPresenter {
	public interface IContactView {
              ...
	}

	private IContactView contactView;

	@Inject
	public ContactPresenter(ContactView contactView) {
....

The flaw in this example is: the presenter gets a concrete ContactView object rather than an IContactView interface injected. The easiest way for getting around this is to introduce a little factory that gets a concrete object injected and returns the desired interface:

public class ContactViewFactory {
	@Inject
	ContactView contactView;

	public IContactView getContactView() {
		return contactView;
	}
}

Now the presenter just lets the factory be injected and gets the IContactView interface:

	private IContactView contactView;

	@Inject
	public ContactPresenter(ContactViewFactory contactViewFactory) {
		contactView = contactViewFactory.getContactView();

Another way would be to provide an OSGi declarative service that registers a class for a given interface with the ContextInjectionFactory. This is how the services like the selection service are registered. Right now, I could not get this working (inject a ContactView to a needed IContactView), but as soon as I find out why or how, I’ll blog about it.

If you want to check out example code from cvs, take a look at the classes DetailsView and DetailComposite in the e4 contacts demo (host: dev.eclipse.org, repository path: /cvsroot/eclipse, user:anonymous, path to bundle: e4/org.eclipse.e4.ui/examples/org.eclipse.e4.demo.contacts).

Have Fun

Kai
Follow me on Twitter

This Post Has 10 Comments

  1. Lars Vogel

    I think we should point out the DI for own objects only works out of the box for objects which are part of the application model.

  2. Kai Tödter

    Hi Lars,

    yes, you are right, but since we do not write applications, advisors, etc. anymore, a big part of the application’s source code is somewhat connected to the workbench model. And as long as we don’t create objects with “new” rather then depending on them, it works in many cases.

  3. Simon Chemouil

    @Lars Vogel

    Not really, using DI is a matter of using an injector in E4 (from org.eclipse.e4.core.di). For instance,

    // keep an instance of the object because the injection framework only keeps WeakReferences and
    // your object will not be valid for injection if you don’t
    this.myObject = InjectorFactory.getDefault().make(MyClassToBeInjected.class, null); // or supply a PrimaryObjectSupplier to your liking to inject custom items

    This will simply create the object/inject with simple “get” (no tracking/updating) then call the method that is annotated with @PostConstruct (if there is one)

    If you want to keep it updated (real @Inject), just call
    InjectorFactory.getDefault().inject(this.myObject, null); // or your PrimaryObjectSupplier

    (stop injection with the method uninject)

    Now, if you want to get values from your IEclipseContext to be injected (eg, parts, model elements, etc), you first need to get it and the prefered way is .. through injection! Afaik it only makes sense to retrieve it from a model object (ie, an entry point to the E4 context/environment).

    To get it, simply have:

    @Inject IEclipseContext context;

    There’s a handy factory there to create objects you want the framework to manage with the IEclipseContext lookup strategy:

    this.injectMe = ContextInjectionFactory.make(InjectMe.class, context);

    ContextInjectionFactory.inject(InjectMe.class, context);

    And here you go! Very concise and extremely powerful (and flexible).

    About using DI with OSGi services, it works out of the box only if the service is present before the DI kicks in. See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=317706
    (I provided an @Dynamic annotation and ExtendedObjectSupplier to make really dynamic OSGi services work with E4 DI).

    Hope this helps,

    Simon

  4. Simon Chemouil

    @Kai, no problem 🙂

    I just noticed the last line of code should read as:

    ContextInjectionFactory.inject(injectMe, context); // the object of course

    Cheers

  5. Tom Schindl

    The ESelectionService stuff is not really a OSGi-Service but is created through an IContextFunction (which is a OSGi-Service :-).

    You might also want to look at IContributionFactory which uses Strings but has the advantage that in future you can create not only JavaObject from it (e.g. JavaScript comes to my mind)

  6. Kai Tödter

    @Tom, yes, you are right, but that’s exactly what I meant. I did not want to go into details until I find out why exactly the same mechanism used for the selection service (using a RegisterFunction DS that registers the selection service implementation class for the interface) is not working in my application. I’ll check in the code soon…

  7. Lars Vogel

    @Simon thanks for the detailed explanation. I wanted to highlight that the magic Kai describes only happens if the object in a model element. Your description is excellent for handling the cases where this is not the case.

    Thank you very much.

  8. Kai Tödter

    With Tom’s help I figured out why the declarative service approach did not work, see my next post…

Leave a Reply

I accept the Privacy Policy