This page describes how the Nuxeo Platform is modular, and how bundles, components and extension points relate to each other to let you create a fully customized application.
One of the main goals of the Nuxeo Platform is to provide an easy and clean way to customize the platform for your application needs. That way:
- No need to hack the system to make it run
- Your custom code will be based on maintained extension points and interfaces, and will be able to be easily upgraded
For that, Nuxeo Platform provides the following patterns:
Bundle: A bundle is a "plugin". It is most of the time a ".jar" file with a specific structure that aims at deploying a new set of features on the Nuxeo server. Thanks to this "bundle" notion, developers can deliver their new features in a standalone JAR that the platform will know how to start. As a result, your customization is also delivered as a plug-in, like the tens of plug-ins that are part of the Nuxeo ecosystem, and that you can find on GitHub or the Nuxeo Marketplace.
Components and services: A component is a software object declared via XML (and that may reference a Java class) that is used to expose some services in the framework. Thanks to this architecture, it is possible to expose a new service anywhere in the Java code executed in the platform. Services are auto-documented: you can see the list on Nuxeo Platform Explorer.
Extensions: An extension is a mechanism leveraged by the services to let platform users inject customization in the core of the implementation. It is a pattern used frequently on products such as Mozilla, Chrome, or Eclipse. Thanks to this architecture, it is possible to go very deep in product customization, only with XML, or using our Nuxeo Studio visual environment, without any coding. You can see the list of all extension points in Nuxeo Platform Explorer. Contributions to extensions are usually delivered in a custom bundle.
Implementing your own bundle, you will be able to contribute to existing extensions so as to customize your application. A lot of these configurations are supported by Nuxeo Studio.
For instance, you can:
- Define custom schemas and Document types and custom lifecycles,
- Add workflows,
- Enforce business policies:
- Update global configurations for the Web UI.
When existing extensions are not enough, you can declare your own services or leverage existing ones in Java code: this will allow your business logics to benefit from the same kind of configurability.
In Nuxeo, everything can be configured:
Inside Nuxeo Platform, software parts are packaged as Bundles. A bundle is a Java archive (JAR) packaged so that it works inside a Nuxeo Application.
It typically contains:
- An OSGi-like manifest file,
- Java classes,
- XML components,
- A deployment descriptor.
The manifest file is used to:
- Define an id for the bundle,
- Define the dependencies of the bundles (i.e.: other bundles that should be present for this bundle to run),
- List XML components that are part of the bundle.
Here is an example of a manifest file, located at
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: NXCoreConvert Bundle-SymbolicName: org.nuxeo.ecm.core.convert Bundle-Localization: plugin Require-Bundle: org.nuxeo.ecm.core.api, org.nuxeo.ecm.core.convert.api Bundle-Vendor: Nuxeo Export-package: org.nuxeo.ecm.core.convert.cache, org.nuxeo.ecm.core.convert.extension, org.nuxeo.ecm.core.convert.service Bundle-Category: runtime Nuxeo-Component: OSGI-INF/convert-service-framework.xml
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, and provides:
- A lifecycle model for Java modules,
- A service model,
- An extension model.
When the Nuxeo runtime starts, it will try to load all Nuxeo bundles installed in the system. When all dependencies of this bundle are resolved, the framework starts the bundle. Starting a bundle means invoking a bundle activator, if any is declared in the manifest file. It also means parsing all Nuxeo components declared in the MANIFEST, and scheduling them for deployment.
In the same way that activations are performed, deactivations are also handled: each step has an equivalent step to undo changes made to the system. This mechanism provides a flexible way to build modular applications, when some resources are made available or are removed.
The mechanism is usually triggered when starting and stopping the Nuxeo server, although hot-reload features are also supported.
Note that Nuxeo is using single class loader for all bundles.
A component is a software object declared via XML (and that may reference a Java class). The corresponding XML file is usually held by a bundle, in the
OSGI-INF directory, and referenced by the
Each component has a unique name and can be used to:
- Declare requirements on other components,
- Declare a Java component implementation,
- Define a service interface,
- Contribute some extensions.
When only contributing some extensions, the component is not backed by a custom Java class: it can be "pure XML" (although some contributions will, themselves, accept Java classes for configured objects). A component is usually made of several classes and XML configuration.
Here is a schema of all the possibilities:
Some components are indeed holding all these different types of declarations, but we usually see the following pattern for "framework" components, defining a service and extension points:
We usually see the following pattern for "contribution" components, defining contributions to other extension points:
Components usually derive from a base class provided by Nuxeo Runtime and will be available as a singleton via a simple Nuxeo Runtime call:
import org.nuxeo.runtime.api.Framework; [...] Framework.getRuntime().getComponent(componentName)
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 through a simple Nuxeo Runtime call:
import org.nuxeo.runtime.api.Framework; [...] Framework.getService(ServiceInterface.class)
To learn more about creating a service, see HOWTO: Create a service.
The list of existing services can be found on the Nuxeo Platform Explorer.
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 provided by Nuxeo Runtime.
This extension point system allows you to:
- Configure the behavior of components (by contributing XML configurations),
- Extend the behavior of components (by contributing 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).
Each Java component can declare one or several extension points, served by one or several service interfaces.
These extension points can be used to provide:
- Additional code (i.e. plug-in system).
So, most Nuxeo services are configurable and pluggable via the underlying component.
Extension points are declared via the XML component that declares the corresponding Java component.
Here is a simple example:
<?xml version="1.0"?> <component name="org.nuxeo.ecm.core.convert.service.ConversionServiceImpl"> <documentation> Service to handle conversions </documentation> <implementation class="org.nuxeo.ecm.core.convert.service.ConversionServiceImpl"/> <service> <provide interface="org.nuxeo.ecm.core.convert.api.ConversionService"/> </service> <extension-point name="converters"> <documentation> This extension point can be used to register new converters </documentation> <object class="org.nuxeo.ecm.core.convert.extension.ConverterDescriptor"/> </extension-point> <extension-point name="configuration"> <documentation> This extension point can be used to configure conversion service </documentation> <object class="org.nuxeo.ecm.core.convert.extension.GlobalConfigDescriptor"/> </extension-point> </component>
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 interface (via the
<implementation>tag) -- the component name and service interface do not need to be identical, like in this example;
- 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 expected for this extension point:
This description is defined directly into these classes by annotations. Nuxeo Runtime instanced descriptors and delivers it to the Java component implementation each time a new contribution to these extension points is detected.
Each Nuxeo extension point uses this pattern to declare configuration possibilities, service integration, extension behavior, merge, override, etc. This pattern is used by all extension points in Nuxeo, and you can use this same infrastructure to declare your own business services.
To learn more about creating an extension point and a related service, see HOWTO: Create a service.
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. This object's javadoc is also visible on the extension point page in the Explorer, see Contribution Descriptors on a sample page.
Here is an example contribution to the extension point presented above:
<?xml version="1.0"?> <component name="org.nuxeo.ecm.platform.convert.plugins"> <extension target="org.nuxeo.ecm.core.convert.service.ConversionServiceImpl" point="converter"> <converter name="zip2html" class="org.nuxeo.ecm.platform.convert.plugins.Zip2HtmlConverter"> <destinationMimeType>text/html</destinationMimeType> <sourceMimeType>application/zip</sourceMimeType> </converter> </extension> </component>
To learn more about contributing to existing extension points, see HOWTO: Contribute to an Extension.
The Nuxeo Platform uses extension points extensively, to let you extend and configure most of the features provided by the platform.
The list of existing extension points can be found on the Nuxeo Platform Explorer.
The Nuxeo main bundles are made available on the Nuxeo server by default. To deploy custom bundles, a Nuxeo Package can be created.
This package can hold bundles, related libraries, as well as configuration templates.
Now, you may want to understand how those packages are deployed on a Nuxeo server.