Eclipse 4 (e4) Tutorial Part 6: Behavior Annotations

In the previous parts of this tutorial series, we described how to create an application model, link those elements to implementations and how to extend the application model. This tutorial and all other parts of the series are now available as a downloadable PDF. In the last part of this tutorial, we provided details about dependency injection. However we focused on how to influence which parameter is injected at a certain place. In many cases, it is additionally important to specify when exactly parameters are injected, or more precisely, when certain methods of a class are called by the framework. Eclipse 4 uses annotations for this purpose. This tutorial describes the most important annotations used in Eclipse 4.

When To Inject?

The annotation @Inject in combination with @Named and @Optional, described in a previous part of this tutorial, is sufficient to control dependency injection for constructors and fields. In both cases, the point in time when objects are injected is clear (instantiation of the class). When methods are marked with only @Inject, these methods are called once after the class is initialized and again every time a parameter changes in the context. However, there are many use cases wherein the developer may want to react to certain events, e.g., if a view gets the focus or if an object is disposed. The Eclipse 3.x interfaces defined methods for these events, e.g., setFocus(), that were called by the framework when a certain event was triggered. In Eclipse 4, views are POJOs, and methods can be named arbitrarily. Therefore, methods that need to be called by the framework at a certain point in time must be marked with corresponding annotations, e.g., @Focus. All described annotations include the dependency injection as @Inject does. That means that if a method is marked with any of the below annotations, all parameters of the method will be injected without an explicit addition of @Inject.

@PostConstruct and @PreDestroy

In many cases, objects need additional initialization after the constructor has been called. This is especially relevant if fields are used. Since fields are injected after the constructor is called, any initialization dependent on fields cannot be done in the constructor.

A typical task for an initialization of an object is the registration listeners. These listeners typically need to be unregistered if the object is not needed anymore. Eclipse 3.x interfaces typically provided methods such as init() and dispose() for this use case. Eclipse 4 uses two standard annotations defined in javax.annotation: @PostConstruct and @PreDestroy.

A method annotated with @PostConstruct is called after a class is initialized with its constructor and after all fields have been injected. A method annotated with @PreDestroy is called when an object is not needed anymore, e.g., when the corresponding view is closed but before the object is destroyed. As mentioned before, all annotations allow the use of additional parameters in these methods, but that is not mandatory. The following code example shows a typical use case. A service is injected as a field and can therefore not be accessed in the constructor. The @PostConstruct method is used to register a listener on the service, the @PreDestroy method to deregister the listener.

@Inject
MyService service;
 
@PostConstruct
public void postConstruct() {
   service.addListener(this);
}
 
@PreDestroy
public void preDestroy() {
   service.removeListener(this);
}

@PostConstruct and @PreDestroy can be used for all classes, which are initialized by the framework or manually using the Injection Factory.

@Focus

For visual elements, e.g., parts, there are additional events to which an implementation should react. A method marked with @Focus is called when the corresponding UI element receives the focus. In SWT applications, the focus must be forwarded to the central SWT element, e.g., a text field or a tree. If the implementation of a view contains several SWT controls, the developer has to choose a control, typically the first text field if it is a form editor.

@Focus
public void onFocus() {
   text.setFocus();
}

@Persist

The annotation @Persist marks a method to be called if a save is triggered on a part. For example, if the parts represent a text editor, the content of the text control is saved into a file.

@Persist
public void save(){
	//save the context of the part
}

The method is typically called from another place then the part itself, e.g., from a handler. The EPartService provides helper methods to save a specific part or all parts that are dirty:

@Execute
public void execute(@Named(IServiceConstants.ACTIVE_PART
 MPart part,EPartService partService) {
   partService.savePart(part, false);
}

@PersistState

A method marked with @PersistState is called before an object is disposed and before the method marked with @PreDestroy is called. The purpose of this method is to persist the latest state of an element if required. If the method is a view, the latest input by the user could be stored for convenience.

@Execute and @CanExecute

There are two additional annotations used especially for handlers, @Execute and @CanExecute. @Execute marks the method to be executed if the handler itself is executed. @CanExecute marks the method responsible for the enable state of the handler. Therefore, the @CanExecute method needs to return a Boolean value, which tells the framework whether the implementation action is currently available or not. As a consequence, Eclipse 4 will enable or disable all menu and toolbar items linked to this handler. As for all annotations, all required parameters are injected.

However, the annotation @CanExecute works quite differently than other annotations. It is not called on a certain event or on a change of one parameter in the context. In fact, in version 4.2, it is called continuously and is timer-based, so it is important to not spend too much execution time within this method.

A very common example for the implementation of a @CanExecute method is a check for the current selection, the active part or the active perspective. The following example checks whether the current selection is of a certain type and enables the handler if it is. The @Execute method invokes a certain action on the current selection:

@CanExecute
public boolean canExecute(@Named(IServiceConstants.ACTIVE_SELECTION)
                      	@Optional Object selection) {
   if (selection!=null && selection instanceof MyObject)
  	return true;
   return false;
}

Lifecycle Annotations

Finally, Eclipse 4 offers the possibility to hook into the lifecycle of a running application. To do this, a lifecycle handler needs to be registered as a property of the registered application:

<property name="lifeCycleURI"               
value="platform:/plugin/helloworld/helloworld.LifecycleHandler">

The implementation of the lifecycle handler itself is a POJO. It supports the following specific annotations:

@PostContextCreate

Is called after the application’s context has been created. Can be used to add or remove objects from the context.

@ProcessAdditions and @ProcessRemovals

Allows the modification of the application model before it is passed to the renderer that will display the application on screen. Allows the addition and removal of application model elements before the application is actually shown.

@PreSave

Is called before the application model is persisted. Allows the modification of the model before saving it.

Conclusion

Behavior annotations of Eclipse 4 allow the specification of the precise point in time when objects are injected. Annotated methods can require parameters but don’t have to. For example, a method annotated with @Focus often does not require any parameters. In this case, it is more important that a focus method is called at a certain point in time when the corresponding UI element gets the focus. Some annotations, such as @Inject, @Named, @PostConstruct and @PreDestroy, are Java standards. Additional annotations, such as @Optional or @Persist, are specific for Eclipse 4.

To get an overview of the source of the available annotations, the following list shows all described annotations with the bundle defining them. If you use any of these annotations, you will need a dependency or a package import to these bundles.

@Active org.eclipse.e4.core.contexts
@Creatable org.eclipse.e4.core.di.annotations
@CanExecute org.eclipse.e4.core.di.annotations
@Execute org.eclipse.e4.core.di.annotations
@Inject javax.inject
@Named javax.inject
@Optional org.eclipse.e4.core.di.annotations
@Persist org.eclipse.e4.ui.di
@PersistState org.eclipse.e4.ui.di
@PostConstruct javax.annotation
@ProcessAdditions org.eclipse.e4.ui.workbench.lifecycle
@ProcessRemovals org.eclipse.e4.ui.workbench.lifecycle
@PostContextCreate org.eclipse.e4.ui.workbench.lifecycle
@PreDestroy javax.annotation
@PreSave org.eclipse.e4.ui.workbench.lifecycle

 

This tutorial and all other parts of the series are available as a downloadable PDF or as links below:

Part 1: Introduction to Eclipse 4

Part 2: Application Model and Views

Part 3: Extending existing models

Part 4: Dependency Injection

Part 5: Soft migration from Eclipse 3.x

Part 6: Behavior Annotations

For more information, contact us:

Jonas Helming and Maximilian Koegel

EclipseSource Munich leads

Author: Jonas Helming

 

Share it

Twitter2
Google+1
LinkedIn
Facebook