Equinox OSGi framework
This page describes OSGi and the technology stack that was used to build iDempiere so that developers and solution architects can see how the system hangs together.
iDempiere and Modularity
iDempiere was created from a fork of Adempiere by converting it to a more modular system based on the Equinox OSGi core framework implementation. Equinox is used as the reference implementation of OSGi, and as such it implements all the required features of the latest OSGi core framework specification, and many of the optional specifications.
The OSGi specification defines a dynamic component system for Java. It is a specification that describes how individual bundles of code, also known as plug-ins, can be connected together in a dynamic way by a services layer that offers a publish-find-bind model for plain old Java objects (POJOs).
In standard Java everything in a JAR may be visible to all other JARs, but with OSGi, everything in that JAR is hidden unless it is explicitly exported in a predetermined manner. There is no sharing by default, so a bundle that wants to use another JAR must explicitly import the parts it needs. Conversely, a bundle must explicitly export functionality that is to be used by other bundles.
OSGi layered architecture
OSGi does this by specifying a layered model to manage plug-ins:
- Bundles: these are the OSGi plug-in components that deliver the application functionality, consisting of POJOs with additional meta-data defined in the MANIFEST.MF file. Bundles hide their internals from other bundles and communicate through well defined services. Bundles are self-contained, reusable units of deployable code, usually in the form of a JAR;
- Services: this layer connects bundles in a dynamic way by offering a publish-find-bind model for the POJOs registered in the service registry. Without OSGi, the standard Java solution is to use static factory methods and Java Reflection to load classes dynamically, resulting is a variety of passive, non-standard APIs and configuration mechanisms;
- Life-Cycle: the API that allows you to list, install, start, stop, update, and uninstall bundles on the fly without bringing down the whole system. Bundles can be managed directly from the OSGi console command line interface and they can be managed remotely;
- Modules: the layer that defines how a bundle's dependencies are managed by importing and exporting code according to the bundle's manifest. Modules are discrete, coarse-grained deployable units;
- Security: the layer that handles security by separating bundles from each other and enforcing permissions to allow or deny bundles access to each other;
- Execution Environment: the underpinning OSGi platform that manages the containers. Like the Eclipse IDE, iDempiere is built on Equinox, which is the reference implementation of the OSGi specification (two other notable implementations of OSGi are Knopflerfish OSGi and Apache Felix).
Modular structure of iDempiere
iDempiere was created by partitioning the monolithic Adempiere application into a large number of plug-ins, each with its own classpath and set of dependencies. If you open the iDempiere codebase with Eclipse, you will see that it consists of dozens of projects: one for each OSGi bundle, or plug-in.
This allows developers to create custom-built plug-ins without touching the upstream codebase. The Equinox OSGi container manages these plug-ins as free-standing, deployable Java objects that interact with the rest of the core iDempiere bundles by means of APIs. The bundles are managed in a service registry.
The advantages are:
- explicit dependency management: iDempiere plug-ins declare what they need and what functionality they provide, whilst otherwise remaining isolated from each other;
- versioned dependency: you can have different versions of the same Java class in different bundles if necessary because your plug-in's dependencies are managed by the OSGi container, not the JVM;
- small footprint: plug-ins are not packaged with all their dependencies, and they follow a lazy activation policy so they are only loaded as and when required;
- easy release: plug-ins can be developed and released independently of upstream developments;
- hot redeploy: individual plug-ins can be redeployed on the fly without affecting the others.
Core iDempiere plug-ins
Now that we have looked at OSGi, let's dig deeper into the structure of iDempiere and look at the core plug-ins that make up the application.
When iDempiere starts, the start-up script runs a Bash shell script idempiere-server.sh which in turn executes the Equinox OSGi launcher org.eclipse.equinox.launcher with the initial executable entry point into the application as a runtime option, namely org.adempiere.server.application. This Java class implements the Equinox IApplication interface and starts the application.
The manifest of org.adempiere.server directs Equinox to load these bundles as dependencies:
- org.eclipse.equinox.app: implementation of the Equinox OSGi Application Container service;
- org.eclipse.jetty.apache-jsp: support for JSP in the embedded Jetty HTTP container;
- org.adempiere.base: the core functionality of iDempiere.
Equinox OSGi Application Container
The Equinox OSGi framework implementation is probably better known as the foundation of the Eclipse IDE platform, but it is actually a standalone OSGi implementation. It is launched by the org.adempiere.server plug-in when iDempiere is started and is responsible for loading all the other iDempiere plug-ins and managing the relationships and dependencies between them.
You can manage bundles through the OSGi console in Eclipse, in a shell console, via telnet, or via HTTP using the Apache Felix console.
Jetty HTTP Server
iDempiere uses the Jetty OSGi infrastructure to embed an HTTP server inside an OSGi container. This hosts the static content, servlets, OSGi web bundles (the OSGi equivalent of WAR files), and provides support for the Java ServerPages (JSPs) used by the original Adempiere code.
In production, it would be wise to put a reverse proxy in front of Jetty, running web application security software such as mod_security. Jetty can expose JMX services which should be monitored in a production environment. The Jetty configuration files are in $IDEMPIERE_HOME/jettyhome/etc/.
This is the base plug-in that provides all the core functionality needed to build iDempiere plug-ins and includes interfaces such as:
- IResourceFinder: finds resources in each plugin;
- IColumnCallout: called whenever a column on a table changes its value;
- IModelValidator: called whenever a record is saved;
- IMenuAction: called whenever a window menu is created;
- IModelFactory: called to create new class models;
- and many others.
The use of the Equinox OSGi framework simplifies the process of developing plug-ins without affecting the trunk to extend the functionality of iDempiere. OSGi also provides two other mechanisms for extending the codebase: fragments and buddy classloading.
A fragment is an OSGi mechanism that allows you to dynamically extend a bundle at runtime. All its classes go into the namespace of the host bundle and it can overwrite classes to change the behaviour of the host. A fragment is not a normal OSGi bundle so no other bundle can be dependent on a fragment. If you want to create a distribution with some bundles and fragments you have to create a "feature" to join them together.
Buddy Classloading is an extension to the OSGi mechanisms from the Eclipse Equinox framework. It allows a normal, full-featured, dependency-enabled bundle to share the classpath namespace with another host bundle.
This hack should not be needed if everything is written according to the OSGi rules, but some older libraries use reflection to find connected classes and depend on one single classloader for the library and the code that uses it. In these cases, buddy classloading can be used where old Compiere/ADempiere APIs that use reflection are still in use. For example, a user may want to change the old Compiere callouts by configuring them in the database, which was the previous Compiere/Adempiere way of doing things before OSGi. Another example is BankStatementMatchers which does not have an extension point as yet.
Here is an example of using Buddy Classloading to develop a callout, but it is not recommended as it is definitely not OSGi best practice.