Skip to main content
Jump to: navigation, search

Riena/Getting Started with injecting services and extensions

< Riena
Revision as of 09:59, 25 August 2008 by Stefan.Liebig.compeople.de (Talk | contribs) (The Idea of Injecting Services and Extensions in Riena)

Riena Project > Getting Started> Getting Started with Injecting Services and Extensions

Getting Started with Injecting Services and Extensions

The Idea of Injecting Services and Extensions in Riena

Services and extensions are the main building blocks of Eclipse RCP applications. There is a long list of discussions and debates about when to use services and when to use extensions. So, we will not add another discussion here. We think that both are useful and have their permission.

One problem with both of them is that using them requires a lot of code that respects their life cycle and is quite error prone. Most of this code is embedded within the code that needs other services or extensions. This approach does not clearly separate concerns and it makes unit testing more difficult.

A common solution for this problem is the use of dependency injection frameworks such as Spring (http://springframework.org/) or HiveMind (http://hivemind.apache.org/).

Within Riena we are using a simple to use approach to inject services and extensions into objects (pojos). Our approach is code based, i.e. no xml configuration files. Furthermore we are using fluent interfaces (http://en.wikipedia.org/wiki/Fluent_interface) and conventions to simplify the injection definitions.

Service injecting examples:

Inject.service("ServiceClazz1").into(target).andStart(context);
Inject.service("ServiceClazz2").useFilter(filter).into(target).bind("register").unbind("unregister").andStart(context);
Inject.service("ServiceClazz3").useRanking().into(target).bind("register").unbind("unregister").andStart(context);

where ´target´ is the pojo that gets a service injected. When services come and go they will be injected into or removed from the pojos. The method names for injecting/removing can be specified with the bind() and unbind() methods. If they are not defined default method names are assumed.

Extensions get injected into pojos as instances of an interface. The interfaces need only to declare the getters. Dynamic proxies will be created for the interfaces which map the data from the extensions. A few simple rules are used for the mapping from extensions to the interfaces (lightweight xml/java mapping). Extension properties. i.e. attributes, sub-elements and element value can then accessed by getters in the interface definition. It is only necessary to define the interfaces for the mapping.

The extension injector does not evaluate the extension schema, so it can only trust that the extension and the interface match with regards to a few basic rules:

  • one interface maps to one extension element type
  • the interface has to be annotated with @ExtensionInterface
  • interface methods can contain getters prefixed with:
    • get...
    • is...
    • create...
  • Such prefixed methods enforce a default mapping to attribute or element names, i.e. the remainder of the methods name is interpreted as the attribute or element name. A simple name mangling is performed, e.g for the method getDatabaseURL the mapping looks for the attribute name databaseURL.

To enforce another name mapping a method can be annotated with @MapName("name"). The name specifies the name of the element or attribute. The extension element´s value can be retrieved by annotating the method with @MapValue(). The return type must be String. The method names of such annotated methods can be arbitrary

  • The return type of a method indicates how the value of an attribute will be converted. If the return type is
    • a primitive type or java.lang.String than the mapping converts the attribute's value to the corresponding type.
    • an interface or an array of interfaces annotated with @ExtensionInterface than the mapping tries to resolve to a nested element or to nested elements.
    • org.osgi.framework.Bundle than the method returns the contributing bundle.
    • java.lang.Class than the attribute is interpreted as a class name and a class instance will be returned.
    • and finally if none of the above matches the mapping tries to create an new instance of the attribute´s value (interpreted as class name) each time it is called. If the extension attribute is not defined null will be returned.

Extension injecting examples:

Inject.extension("extpoint1").into(target).andStart(context);
Inject.extension("extpoint2").useType(interface).into(target).bind("configure").doNotReplaceSymbols().andStart(context);
Inject.extension("extpoint3").expectExactly(1).into(target).andStart(context);

A sample interface from Riena:

/**
 * Interface for a SubModuleType extension that defines how an activated
 * submodule appears in the work area.
 */
@ExtensionInterface
public interface ISubModuleTypeDefinition extends ITypeDefinition {

	/**
	 * @return A controller that controlles the UI widgets in the view through
	 *         ridgets (see org.eclipse.riena.ui.ridgets.IRidget)
	 */
	IController createController();

	/**
	 * Indicates whether the view is shared i.e. whether one instance of the
	 * view should be used for all submodule instances.
	 * 
	 * @return true if the specified view should be a shared view, false
	 *         otherwise
	 */
	boolean isShared();

	/**
	 * @return For the SWT-based Riena UI this is the ID of the view associated
	 *         with the submodule. Must match the ID field of an
	 *         "org.eclipse.ui.view" extension.
	 */
	String getView();

	Bundle getContributingBundle();

}

That gets injected into:

public class PresentationExtensionInjectionHelper<E extends ITypeDefinition> {

	private E[] data;

	public void update(E[] data) {
		this.data = data.clone();

	}

	public E[] getData() {
		return data.clone();
	}

}

by executing:

Inject.extension("org.eclipse.riena.navigation.subModuleType").useType(ISubModuleTypeDefinition.class).into(target).andStart(Activator.getDefault().getContext());

Back to the top