Here is an example of a MANIFEST file:
Here we can see that this bundle:
- Is named
- Depends on two other bundles:
- Contains one XML component:
Nuxeo bundles are deployed on the Nuxeo server via the Nuxeo runtime that behaves partially like an OSGi framework. An OSGi framework provides:
- A life cycle model for Java modules,
- A service model.
When an OSGi framework starts it will try to load all bundles installed in the system. When all dependencies of a bundle are resolved, the framework starts the bundle. Starting a bundle means invoking a bundle activator if any is declared in the manifest file.
This way each bundle that registers an activator is notified that it was started - so the bundle activator can do any initialization code required for the bundle to be ready to work. In the same way when a bundle is removed the bundle activator will be notified to cleanup any held resources. OSGi frameworks provides listeners to notify all interested bundles on various framework events like starting a bundle, stopping another one, etc.
This mechanism provides a flexible way to build modular applications which are composed of components that need to take some actions when some resources are become available or are removed. This life cycle mechanism helps bundles react when changes are made in the application. Thus, an OSGi bundle is notified when all its dependencies were resolved and it can start providing services to other bundles. OSGi is also proposing a service model - so that bundles can export services to other bundles in the platform.
There are two major differences between the default Nuxeo Runtime launcher and an OSGi framework:
- Nuxeo is using single class loader for all bundles. It doesn't interpret OSGi dependencies in the manifest.
- Nuxeo services are not exposed as OSGi services.
Components and Services
A component is a piece of software defined by a bundle used as an entry point by other components that want to contribute some extensions or to ask for some service interface. A component is an abstract concept - it is not necessarily backed by a Java class and is usually made from several classes and XML configuration.
The XML components are XML files, usually placed in the
OSGI-INF directory, that are used to declare configuration to Nuxeo Runtime.
Each XML component has a unique id and can:
- Declare requirement on other components,
- Declare a Java component implementation,
- Contain a XML contribution,
- Declare a Java contribution.
A Java component is a simple Java class that is declared as component via an XML file.
Components usually derive from a base class provided by Nuxeo Runtime and will be available as a singleton via a simple Nuxeo Runtime call:
Usually, components are not used directly, they are used via a service interface. For that, the XML components can declare which service interfaces are provided by a given component. The component can directly implement the service interface or can delegate service interface implementation to an other class. Components must be accessed using the interfaces they provide and not through real implementation classes. Once declared the service will be available via a simple Nuxeo Runtime call:
One of the corner stones of the Nuxeo Platform is to provide components and services that can easily be configured or extended. For that, we use the extension point system from Nuxeo Runtime that is inspired from Equinox (Eclipse platform).
This extension point system allows you to:
- Configure the behavior of components (i.e. contribute XML configuration),
- Extend the behavior of components (i.e. contribute Java code or scripting).
Basically, inside the Nuxeo Platform, the pattern is always the same:
- Services are provided by components,
- Components expose extension points.
The same extension point system is used all over the platform:
- Inside Nuxeo Runtime itself,
- Inside Nuxeo Core (configure and extend document storage),
- Inside the Nuxeo Service layer (configure and extend ECM services),
- Inside the UI layer (assemble building blocks, contribute new buttons or views, configure navigation, ...).
Each Java component can declare one or several extension points.
These extension points can be used to provide:
- Additional code (i.e.: plugin system).
So most Nuxeo services are configurable and pluggable via the underlying component.
Declaring an Extension Point
Extension points are declared via the XML component that declares the Java component.
Here is a simple example:
What we can read in this XML component is:
- A Java component (via the
<component>tag) with a unique id (into the
nameattribute) is declared;
- This component declares a new service (via the
- The declaration of the
ConvertServiceinterface (used to also fetch it) implemented by
- This service expose two extension points:
- One to contribute configuration (
- One to contribute some Java code (new
- One to contribute configuration (
Each extension point has his own XML structure descriptor, to specify the XML fragment he's waiting for into this extension point:
This description is defined directly into these classes by annotations. Nuxeo Runtime instanced descriptors and delivers it to the service each time a new contribution of these extension points is detected.
Each Nuxeo extension point uses this pattern to declare configuration possibilities, service integration, behavior extension, etc.
You understand this pattern, you will understand all extension points in Nuxeo. And you can use this infrastructure to declare your own business services.
Contributing to an Extension Point
XML components can also be used to contribute to extension points.
For that, the XML component needs to:
- Be referenced in a manifest bundle,
- Specify a target extension point,
- Provide the XML content expected by the target extension point.
Expected XML syntax is defined by the XMap object referenced in the extension point declaration.
Here is an example contribution to an extension point:
Extension Points Everywhere
The Nuxeo Platform uses extension points extensively, to let you extend and configure most of the features provided by the platform (see all extension points available in the platform for instance).
All the bundles included in a Nuxeo Application are part of different plugins (from the core plugins to the high level ones). A minimal application is represented by a single plugin - the framework itself (which is itself packaged as a bundle).
This is what we are naming Nuxeo Runtime. Of course launching the Nuxeo Runtime without any plugin installed is useless - except a welcome message in the console nothing happens. But, starting from Nuxeo Runtime you can build a complete application by installing different plugins (depending on the type of your application you may end up with tens of bundles).
A basic Nuxeo Application is composed at least of two layers of plugins: the Runtime layer and the core one.
Packaging and Deployment
The layered architecture impacts the way we package features in the Nuxeo Platform.
In order to keep as much deployment options as possible and let you choose what you deploy and where, each feature (workflow, relations, conversions, preview ...) is packaged in several separated bundles.
Typically, this means that each feature will possibly be composed of:
- An API Bundle that contains all interfaces and remotable objects needed to access the provided services;
- A Core Bundle that contains the POJO implementation for the components and services;
- A Facade Bundle that provides the JEE bindings for the services (JTA, Remoting, JAAS ...);
- A Core Contrib Bundle that contains all the direct contributions to the Repository (Document types, listeners, security policies ...);
- Client bundles.
All the bundles providing the different layers of the same feature are usually associated to the same Maven artifact group and share the same parent POM file.
This is basically a bundle group for a given feature.
Now you may want to understand how those packages are deployed on a Nuxeo server.