This tutorial gives an overview how to use the Eclipse IEclipseContext in an RCP application or in an Eclipse plug-in.
1. Accessing and extending the Eclipse context
1.1. Accessing the context
You can place objects directly in the
IEclipseContext
hierarchy to make them available to other model objects.
To access an
existing
context you can use dependency injection if
the
relevant
object is
managed by the Eclipse runtime.
This is the case
for
all model objects. The following code demonstrates how to get access
to the active
IEclipseContext
, in which the handler is called.
package com.vogella.tasks.ui.handlers;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Execute;
public class ShowMapHandler {
@Execute
public void execute(IEclipseContext context) {
// add objects to the active local context injected into
// this handler
// ...
}
}
If a model object implements
MContext
,
you can use dependency injection to get the model object injected
and
call the
getContext()
method to access its context. For example, MPart, MWindow,
MApplication and MPerspective extend MContext.
The following code demonstrates how to get the
MApplication
injected and how to access its
IEclipseContext
.
package com.vogella.tasks.ui.parts;
import jakarta.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.swt.widgets.Composite;
public class TodoDetailsPart {
@PostConstruct
public void createControls(Composite parent,
MApplication application) {
// getting the IEclipseContext of the application
// via the MApplication object
IEclipseContext context = application.getContext();
// add or access objects to and from the application context
// ...
}
}
1.2. Objects and context variables
You can add key / value pairs directly to the
IEclipseContext
.
Adding objects to
a context can be done via
the
set()
method of the
IEclipseContext
interface.
The following example creates a new context via the
EclipseContextFactory.create()
factory method call and adds some objects to it. Via the
setParent()
method call, the new context is connected to the context hierarchy.
@Inject
public void addingContext(IEclipseContext context) {
// create a new IEclipseContext instance
IEclipseContext myContext = EclipseContextFactory.create();
// add objects to context
myContext.set("mykey1", "Hello1");
myContext.set("mykey2", "Hello2");
// adding a parent relationship
myContext.setParent(context);
// alternatively you can create a new
// context which has a parent/child
// relationship via the
// context.createChild() method call
}
Such a context can be used to instantiate an object via the Eclipse framework.
A
context variable
is a key which is
declared as
modifiable
via the
declareModifiable(key)
method call.
@Inject
public void addingContext(IEclipseContext context) {
// putting in some values
context.set("mykey1", "Hello1");
context.set("mykey2", "Hello2");
// declares the named value as modifiable by descendants of this context
// if the value does not exist in this context,
// a null value is added for the name
context.declareModifiable("mykey1");
}
Context variables are added to particular levels of the
IEclipseContext
hierarchy and can also be modified using the
modify()
method rather than
set()
method of the
IEclipseContext
.
The
modify()
method searches up the chain to find the
IEclipseContext
defining the variable. If no entry is found in the context hierarchy,
the value will be
set in the
IEclipseContext
in which the call started.
If the key already exists in the context, then modify()
requires that the key has been set to modifiable with the declareModifiable()
method, if not, the method throws an exception.
You can add key/value pairs and Context variables at different levels of the context hierarchy to supply different objects in your application.
1.3. Replacing existing objects in the IEclipseContext
Instead of adding new objects to the
IEclipseContext
hierarchy, you can also override existing objects by using
the same
key.
You can change behavior of your application by overriding
certain
entries in the context.
For example, you can modify
the context of the
MWindow
model element. Its
IEclipseContext
is originally created by the
WBWRenderer
class. By default it puts an instance
of the
IWindowCloseHandler
and the
ISaveHandler
interface
into the local context of the
MWindow
model element. The
IWindowCloseHandler
object is responsible for
the
behavior once the
MWindow
model element is closed. The default
IWindowCloseHandler
prompts the user if he wants to save
dirty parts (editors with changed
content). You can change this default
implementation by replacing the
object in the context. The following example shows an
@Execute
method in a handler implementation which overrides this class at
runtime.
@Execute
public void execute(final Shell shell, EModelService service,
MWindow window) {
IWindowCloseHandler handler = new IWindowCloseHandler() {
@Override
public boolean close(MWindow window) {
return MessageDialog.openConfirm(shell,
"Close",
"You will loose data. Really close?");
}
};
window.getContext().set(IWindowCloseHandler.class, handler);
}
You could use this example in your life cycle handler and subscribe
to the
UIEvents.UILifeCycle.APP_STARTUP_COMPLETE
event. In the event handler you would replace the
IWindowCloseHandler
handler in the context.
|
1.4. Accessing the IEclipseContext hierarchy from OSGi services
OSGi services are not directly part of the
IEclipseContext
hierarchy and are created by the OSGi runtime. The OSGi runtime does
not support dependency injection based on the
@Inject
annotation.
The Eclipse framework
registers the implementation of the
MApplication
interface
also as an OSGi service. This allows OSGi services to use
the OSGi API to access the
MApplication
and its context via the
getContext()
method. As the
EModelService
is part of the
MApplication
context you can search for other context elements via
it.
1.5. Model add-ons
To participate in dependency injection with your custom Java
objects
you can add them as model add-ons
to the
application model.
The classes
referred to by the
model add-ons can
access and modify the
IEclipseContext
or
interact with other services,
e.g., the event system.
The following screenshot shows a custom model add-on registered in the application model.
The following code shows an example implementation for the model
addon class. This addon places an object into the
IEclipseContext
package com.vogella.tasks.ui.addons;
import jakarta.annotation.PostConstruct;
import org.eclipse.e4.core.contexts.IEclipseContext;
public class MyModelAddon {
@PostConstruct
public void init(IEclipseContext context) {
// injected IEclipseContext comes from the application
context.set("test1", "Hello");
}
}
1.6. RunAndTrack
The
IEclipseContext
allows you via the
runAndTrack()
method to register a Java object of type
RunAndTrack
.
A
RunAndTrack
object
is basically a
Runnable
which has access to the context.
If the context changes, the
RunAndTrack
is called by the Eclipse framework.
The runnable does not need to be
explicitly unregistered from
this
context when it is
no
longer
interested in tracking changes. If
the
RunAndTrack
is invoked by the Eclipse platform and it
returns
false
from its
RunAndTrack.changed()
method, it is
automatically unregistered
from change tracking on
this
context.
Such a
RunAndTrack
object
allows a client to keep some external state
synchronized
with
one
or more values
in this context.
2. Extending the objects available for the dependency injection
2.1. Creating and injecting custom objects
Using dependency injection for your custom objects has two flavors.
-
You want to create objects which declare their dependencies with
@Inject
based on a anIEclipseContext
context. See chapter: Using dependency injection to create objects for details. -
You want the Eclipse dependency container to create your custom objects automatically on demand and then get them injected into your model objects. See chapter: Create your custom objects automatically with @Creatable and chapter: Create automatically objects in the application context with @Singleton.
2.2. Using dependency injection to create objects
Using dependency injection is not limited to the objects created by
the
Eclipse runtime. You can use the same approach
to create an instance of a given class based on a given
IEclipseContext
. The given class can contain
@Inject annotations.
For this you use the
ContextInjectionFactory
class as demonstrated in the following code example.
// create instance of class
ContextInjectionFactory.make(MyJavaObject.class, context);
The
ContextInjectionFactory.make()
method creates the object. You can also put it into the
IEclipseContext
hierarchy
after the creation. If you place it into the
IEclipseContext
of the application, the created object is globally available.
For this you can either use an existing
IEclipseContext
or create a new
IEclipseContext
.
The new context object can be connected to the context hierarchy.
Using a new context might be preferable to
avoid
collision of keys and
to
isolate your changes in a local
context. Call the
dispose
method on your local context, if the object is not needed anymore.
The following code demonstrates how to create a new
IEclipseContext
object and to place values into it. This context can be used to
create a new object.
IEclipseContext context = EclipseContextFactory.create();
// add your Java objects to the context
context.set(MyDataObject.class.getName(), data);
context.set(MoreStuff.class, moreData);
// dispose the context if you are done with it
context.dispose();
The next code example demonstrates how you can connect your new
IEclipseContext
object with an existing context hierarchy. The factory searches
the
hierarchy upwards to find values, requested by the
class which is
instantiated.
@Inject
public void createObjectInPart(IEclipseContext ctx) {
// create a new local_ context
IEclipseContext localCtx =
EclipseContextFactory.create();
localCtx.set(String.class, "Hello");
// connect new local context with context hierarchy
localCtx.setParent(ctx);
// create object of type MyJavaObject via DI
// uses the localCtx and searches upwards for required objects
MyJavaObject o = ContextInjectionFactory.make(MyJavaObject.class,
localCtx);
//TODO do something with the "o" object
}
The
ContextInjectionFactory.inject(Object, IEclipseContext)
method allows you to perform injection on an existing object. For
example, if you created the object with the
new()
operator, you can
still run dependency injection on it.
2.3. Create your custom objects automatically with @Creatable
If you want the Eclipse framework to create your custom objects
for
you,
annotate them with
@Creatable
. This way you are telling the Eclipse DI container that it should
create
a new instance of this object if it does not
find an instance
in the
context. The automatically-generated instance is not stored in
the context.
If you have a non default-constructor, you must use
the
@Inject
annotation
on the constructor to indicate that Eclipse should try to
run
dependency injection on it.
For example, assume that you have the following domain model.
package com.vogella.tasks.ui.creatable;
import org.eclipse.e4.core.di.annotations.Creatable;
@Creatable
public class Dependent {
public Dependent() {
// placeholder
}
}
package com.vogella.tasks.ui.creatable;
import jakarta.inject.Inject;
import org.eclipse.e4.core.di.annotations.Creatable;
import com.vogella.tasks.model.TaskService;
@Creatable
public class YourObject {
// constructor
@Inject
public YourObject(Dependent depend, TaskService service) {
// placeholder
}
}
As the Eclipse framework is allowed to create instances of the
Dependent
and the
YourObject
class, it can create them if an instance is requested via
dependency
injection. In this example the arguments of the
constructors can be
satisfied.
If no fitting constructor is found, the
Eclipse framework
throws
an
exception.
Assuming that you have defined the
TaskService
OSGi
service in your application, you
can get an instance of your
YourObject
class injected into a part. The following example code demonstrates
that.
// add this for example to your playground part
@Inject
public void setYourObject(YourObject object) {
System.out.println(object);
}
2.4. Create automatically objects in the application context with @Singleton
If the object should be created in the application context, use the
@Singleton
annotation in addition to the
@Creatable
annotation. This ensures that only one instance of the object is
created in your application.
package com.vogella.tasks.ui.creatable;
import jakarta.inject.Inject;
import org.eclipse.e4.core.di.annotations.Creatable;
import com.vogella.tasks.model.TaskService;
@Creatable
@Singleton
public class YourObject {
// constructor
@Inject
public YourObject(Dependent depend, TaskService service) {
// placeholder
}
}
3. Learn more and get support
This tutorial continues on Eclipse RCP online training or Eclipse IDE extensions with lots of video material, additional exercises and much more content.
4. Eclipse 4 resources
4.1. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting