Achieving Inversion of Control with Eclipse RCPby Riccardo Govoni
Eclipse Rich Client Platform (RCP) is a powerful software foundation, based on interconnected and collaborating plugins, that allows the developer to build generic applications. RCP leaves the developer free to concentrate on the code that performs the application business, instead of spending time reinventing the wheel to write the application managing logic.
Inversion of Control (IoC) and Dependency Injection (DI) are programming patterns that can be used to reduce coupling in computer programs. They follow a simple principle: you do not create your objects; you describe how they should be created. You do not instantiate or directly locate the services needed by your components; instead you describe which services are needed by which components, and someone else (generally a container) is responsible for hooking it all together. This is also known as the Hollywood principle: don't call us--we'll call you.
This article will describe a simple way to introduce support for Dependency Injection into an Eclipse RCP application. To avoid polluting the Eclipse platform infrastructure and to transparently add the IoC framework on top of the RCP, we will use a combination of runtime bytecode manipulation techniques (using the ObjectWeb ASM library), Java classloading agents (using the
java.lang.instrument package), and Java annotations.
What is Eclipse Rich Client Platform?
In a single sentence, Eclipse Rich Client Platform is a set of libraries, software frameworks, and a runtime environment for building applications that are both stand-alone and strongly interconnected with the network.
Although Eclipse was conceived as a framework to build Integrated Development Environments (IDEs), the whole product has been refactored (starting from the 3.0 release) into various separated components, so that a minimum subset of these components can be used to build arbitrary applications. Such a subset makes up the Rich Client Platform and comprises different elements: the basic runtime, the user interface components (SWT and JFace), the plugin, and OSGi layer. Figure 1 shows the main components of the Eclipse platform.
Figure 1. The Eclipse platform's main components
The entire Eclipse platform is based on the key concepts of plugins and extensions points. A plugin is the smallest unit of function that can be developed and delivered separately. It is typically packaged as a jar file and extends the platform by adding a functionality (e.g., an editor, a toolbar button, or a compiler). The whole platform is simply a set of interconnected and communicating plugins. An extension point is an available interconnection endpoint that other plugins may use to provide added functionalities (extensions in Eclipse terms). Extensions and extension points are defined in XML configuration files bundled with the plugins.
While the plugin paradigm leverages important patterns such as Separation of Concern, the strong interconnection and communication that plugins need can lead to wired dependencies between them. Typical examples arise from the need to locate singleton services available to the application, such as the database connection pool, the logging handles, or the user-saved preferences. Inversion of Control and Dependency Injection are viable solutions to remove such dependencies.
Inversion of Control and Dependency Injection
Inversion of Control is a programming pattern that focuses on the separation between how services (or application components) are defined and how they should locate the other services they depend on. To achieve such separation, implementations usually depend on a container, or locator framework, which is responsible for various tasks:
- keeping a collection of available services
- providing a means to bind together components (serviceable objects) with the services they depend on
- supplying a way for the application code to request a configured object (i.e., an object whose dependencies have all been satisfied), which can therefore be used with the guarantee that all related services are available to the object
Existing frameworks actually use a combination of three basic techniques to perform the binding between services and components:
- Type 1 (interface-based): serviceable objects need to implement a dedicated interface that provides them with an object from which they can look up dependencies (other services). This is the pattern used by the earlier containers provided by Excalibur.
- Type 2 (setter-based): services are assigned to serviceable objects via JavaBeans properties (setter methods). Both HiveMind and Spring use this approach.
- Type 3 (constructor-based): services are provided as constructor parameters (and are not exposed as JavaBeans properties). This is the exclusive approach used by PicoContainer. It is also used in HiveMind and Spring.