Nuxeo Runtime

Table of Contents

28.1. Overview
28.1.1. Main Goals
28.1.2. Main Features
28.2. What is OSGi?
28.3. OSGi Support
28.3.1. Supported Features
28.3.2. Unsupported Features
28.3.3. Planned Features
28.4. Component Model
28.4.1. What are components?
28.4.2. Main Features
28.4.3. Planned Features
28.4.4. Adapting Components
28.4.5. Flexible Model
28.4.6. Component Life Cycle
28.4.7. Component Extensibility
28.5. Supported Host Platforms
28.5.1. JBoss Integration
28.5.2. Eclipse Integration
28.6. Using Nuxeo Runtime
28.6.1. Creating Components
28.6.2. Using components
28.6.3. XML Component Descriptors
28.7. Integration tests for Nuxeo Runtime applications
28.7.1. The NXRuntimeTestCase base class
28.7.2. Frequent patterns
28.8. Detailed Architecture
28.9. References

28.1. Overview

Nuxeo Runtime is the foundation of the Nuxeo infrastructure. It handles deployment and extensibility of components to target platforms. This component allows the whole Nuxeo infrastructure to be easily ported between Java platforms (Java EE, OSGi, etc.) and features an easy plug-in mechanism that any component can use to declare extension points. These extension points can be used by other components to extend the former one.

The Nuxeo Runtime uses the OSGi component model and a set of adapters to deploy POJO components to Java host platforms, such as Eclipse/Equinox, or a Java EE 5 application server such as JBoss or WebLogic. When deployed, Nuxeo Runtime components become actual host platform components. For example on JBoss the component is seen as a MBean, while when deployed on Geronimo it is seen as a GBean and on Eclipse it is seen as a native Eclipse plug-in. In short, Nuxeo Runtime offers a new and seamless way to make your Java EE applications and components extensible (as Eclipse developers are already used to).

Nuxeo Runtime is not specific to the Nuxeo platform, it is a generic deployment and extension system that can be used in any Java or Java EE application.

Forget specific build of your applications for a dedicated project or customer and enjoy “Code once, deploy anywhere” for real!

28.1.1. Main Goals

One of the main requirements of the “Nuxeo Core” component is to be deployable on both the JBoss and Eclipse platforms. To ease development and allow as much code reuse as possible, Nuxeo developed a common component and packaging model that may be deployed and used on both of these platforms without any code change or repackaging. This model, the Nuxeo Runtime, was developed as the foundation for all Nuxeo components. The Nuxeo Runtime is not a standalone framework. It is, in short, a component model running on top of an existing platform that provides a common, platform-independent, model to underlie the components of an application. This architecture allows flexible and true componentization of applications.

In addition to the component model, the Nuxeo Runtime also defines a common model for packaging, the OSGi bundle model. At the lowest level, OSGi bundles are simply regular JARs containing an OSGi manifest file. OSGi technology is becoming more and more popular and is currently used by Eclipse, Geronimo, Glassfish, and Jonas as their runtime framework. Because of the component and packaging choices, applications based on Nuxeo Runtime can run on different platforms without modification and without having to care about the particulars of a deployment platform.

28.1.2. Main Features

The main features provided by Nuxeo Runtime are:

  1. Native OSGi support

  2. Extensible component model through extension points

  3. Adapters to support host platforms (JBoss and Eclipse support is built-in)

28.2. What is OSGi?

OSGi (Open Services Gateway initiative) is an open standards organization founded by Sun Microsystems, IBM, Ericsson and others in March 1999. OSGi defines a modular and complete, Java-based service framework. The deployment units used by this framework are called bundles, so we will refer them as OSGi bundles.

OSGi bundles are normal Java libraries (JAR files) containing a special manifest file (META-INF/MANIFEST.MF) describing all aspects related to the bundle, for example: the bundle name, description, bundle dependencies, exported packages, the bundle classpath, the bundle activator and many other OSGi-defined features.

Here is a typical OSGi manifest file:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: NxRuntimeEclipseDemo Plug-in
Bundle-SymbolicName: org.nuxeo.runtime.demo.eclipse.Demo; singleton:=true
Bundle-Version: 1.0.0
Bundle-Activator: org.nuxeo.runtime.demo.eclipse.demo.Activator
Bundle-Vendor: Nuxeo
Bundle-Localization: plugin
Require-Bundle: org.eclipse.ui,
  org.eclipse.core.runtime,
  org.nuxeo.runtime.demo.HelloWorld,
  org.nuxeo.runtime

The bundle activator is a Java object that is called whenever the bundle is started and stopped by the framework. This is the only way available to the application to access the framework functionality.

Besides bundles and bundle management, OSGi provides a service registry: an API to register the services provided by a bundle and to lookup services defined by other bundles. Also, OSGi defines a Declarative Services specification that significantly simplifies the service-oriented programming model. Through this model, services can be defined in XML files inside the bundle and automatically deployed by the framework.

For a complete definition of OSGi, see Wikipedia.

A key goals of Nuxeo Runtime is to natively support various implementations of the OSGi framework and to use the OSGi bundle model for packaging and deployment. Another goal is to align the Nuxeo Runtime component model with the OSGi-notion of Declarative Specification.

28.3. OSGi Support

Nuxeo Runtime provides "built-in integration" with any OSGi-compliant framework. This means Nuxeo Runtime-based components can run “as is” on any OSGi-enabled platform. On other platforms like JBoss, an adapter is required. The Nuxeo Runtime eases the creation of such adapters by providing an abstract OSGi adapter that can be customized for any platform.

Note: This does not mean you can transform any platform into a fully OSGi-compliant platform using Nuxeo Runtime adapters. Primarily, this is because the adapter must use the host platform's class-loading and deployment model that may be incompatible with the OSGi specifications. The Nuxeo Runtime's adapters only mimic an OSGi environment, using native host platform features, for applications using Nuxeo Runtime. Many OSGi features are not yet provided by the adapter – but we hope to add more and more features. If you are interested in helping on this, do not hesitate to contact us :-). Currently, one of the most important features that is missing is OSGi service support, but we are working on this and hope to provide it soon.

When running on true OSGi platforms, no adapter is used and thus all OSGi features are available and supplied by the host platform. Nuxeo Runtime components run on such a platform without any alteration.

Currently we provide two built-in adapters:

  1. JBoss OSGi adapter – used to deploy OSGi bundles on JBoss AS 4.x

  2. Test OSGi adapter – used for JUnit testing and can be used on any simple Java application that is not using a complex class loading or deployment mechanism.

28.3.1. Supported Features

Currently, Nuxeo Runtime adapters can provide the following OSGi features:

  1. OSGi Bundle deployment

  2. Manifest file loading

  3. Classpath processing

  4. BundleActivator support (activation and deactivation)

  5. BundleActivator notification each time a bundle is started and stopped

  6. Fake Bundle and BundleContext implementations that adapt OSGi operations to native operations of the host platform

  7. Support for the common operations defined by the OSGi API for BundleActivators

  8. Bundle lifecycle and framework support

  9. Bundle dependencies (as specified in the manifest)

28.3.2. Unsupported Features

The following OSGi features are not supported (yet):

  1. The OSGi service layer

  2. The OSGi security layer

  3. The OSGi class-loading specifications (the class-loading mechanism of the host platform is used)

  4. Some methods of the interfaces Bundle and BundleContext (unimplemented Methods will thrown an UnsupportedOperationException exception)

28.3.3. Planned Features

  1. Supporting the OSGi service layer

  2. Implementing the OSGi declarative services based on the runtime component model

28.4. Component Model

The component model provides a flexible way to define, register and locate components. It was designed in order to reuse the same components on very different platforms like JBoss and Eclipse.

Full support of OSGi declarative service specifications is planned for the medium-term future. In addition to this, Nuxeo components can describe any type of components, not only services.

28.4.1. What are components?

A definition from Wikipedia: “A software component is a system element offering a predefined service and able to communicate with other components”. Components as defined by the Nuxeo model are logical units that may depend on and/or extend one another. The Nuxeo Runtime is responsible for providing a common API to register, locate or extend components and most commonly components are registered using XML descriptor files.

Components can be declared as independent, top-level components - by using a standalone XML file in the bundle- or they can be declared at a finer granularity by programmatically registering sub-components within the bundle. To declare an top-level component, you need to create an XML description file, put it somewhere in the bundle, typically in the OSGI-INF directory, and specify the “Nuxeo-Component” header in the bundle manifest to load the component at bundle activation. For example, the manifest shown below has a reference to the XML file "helloworld-extension.xml" that declares a component.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HelloWorldExtension Plug-in
Bundle-SymbolicName: org.nuxeo.runtime.demo.HelloWorldExtension
Bundle-Version: 1.0.0
Bundle-Vendor: Nuxeo
Bundle-Localization: plugin
Require-Bundle: org.nuxeo.runtime.demo.HelloWorld
Nuxeo-Component: OSGI-INF/helloworld-extension.xml

28.4.2. Main Features

  1. Declarative components through XML descriptors: XML component descriptors are tightly integrated with OSGi – you can specify which components should be deployed at bundle activation-time by using the custom manifest header “Nuxeo-Component”.

  2. Dependency between components: components are activated only when all their prerequisites are met. If prerequisites are not met the components will be put in a pending state until their dependencies are completely resolved. Similarly when uninstalling a component, all the components depending on it will be moved to the pending state when the a component is deactivated.

  3. Extensibility through extension points: each component can let other components extend its functionality by defining a set of extension points. Other components (or the component itself) may then plug extensions (Java code) into one of the declared extension points. This flexible extension mechanism draw inspiration from the Eclipse extension points but is not identical to it.

  4. Life Cycle Events: component life cycle events are sent by the runtime to any interested component. See Adaptable Components for a common use case.

  5. OSGi integration: the component model is about to be fully integrated with OSGi and will be soon compliant with the OSGi declarative service model.

  6. Platform Independence: the component model can be used on any platform. It provides a single API to register and look-up components – the Nuxeo Runtime native API may be used (and in the future, the OSGi service API will be available too).

28.4.3. Planned Features

  1. Complete integration with OSGi declarative services specifications

  2. Component lookup through JNDI

28.4.4. Adapting Components

The runtime implementation may "adapt" registered components to components of the host platform. (This can often be accomplished using the Nuxeo component life cycle notifications.) The JBoss adapter for the Nuxeo Runtime, for example, is already doing this to adapt runtime components into JBoss "MBean services", and thus Nuxeo "components" are seamlessly integrated into the host platform. Since these components are understood by the host platform, we can leverage existing host platform functionaly, such as MBean service management on JBoss.

28.4.5. Flexible Model

You should not be afraid by the “component model” denomination. The runtime component model does not limit your objects, nor imposing any extra rules on your development practices. You do not need to modify your existing objects to derive from some Nuxeo-supplied base class, nor implement some runtime interfaces in order to plug your objects into the component registry. Your objects that implement a "component" may be of any kind.

The only requirement is that to be a "component", your object must have a public constructor without arguments (the default constructor) so that it can be instantiated via newInstance method on the your object's Class object. The component model makes this a mild restriction, since it supplies other means for object initialization if you need to do complex actions at the time your object is created.

If you want to benefit from the extension points mechanism or to respond to component life cycle events like activation or deactivation, there are two options. You can either implement the Component interface if this is convenient for you, or define some methods with a particular signature so your object will be called by the runtime using Java reflection. See below for more details on this.

In conclusion, the component model offers substantial benefits with few requirements; the model primarily provides you the capability to register your components, extend others components, allow others to extend your componentns, and look up components. These functions work the same way on any platform supported by Nuxeo Runtime.

28.4.6. Component Life Cycle

A component has three primary life cycle states:

  1. Registered: the component registration information was created and inserted into the registry. The component dependencies are not yet processed or resolved, so the component cannot be activated.

  2. ResolvedAll: dependencies of this component are satisfied. The component can be safely activated.

    Other unresolved components waiting for a resolution that depends on this component are notified and if they have no more dependencies, they will be moved to this state as well.

  3. ActivatedComponent: Activation occurs as a result of one of three events: immediately upon component becomin resolved, programmatically at the user request, or lazily the first time the component is referred to by another component. The only requirement for activation is that the component must be resolved.

    Currently only the immediate activation mode is supported.

    When an activated component is deactivated it is put back into the resolved state. If a components is to become unregistered, it will first be put in the resolved state, then an unresolved event is fired and the component regresses to the registered sate, and finally it is removed from the registry.

    When a component is activated or deactivated the runtime will call the activate or the deactivate method of the component, if any. Implementing life cycle methods is the programmer's choice, they are not required. These methods can be used to initialize and destroy the component in the given context.

    The activation of a component signifies that the component is available and ready to be used by other components, so that the component must be correctly initialized when it enters this state. The following diagram illustrates the life cycle states, method call, and messages sent for Nuxeo Runtime components.

    1. XXX ADD GRAPHIC HERE

28.4.7. Component Extensibility

A key innovation of the Nuxeo Runtime component model is the extension mechanism that enables components to extend one another.

We will begin with a demonstration of how this mechanism works: Imagine you have a component A, implemented in Java with class ImplA, that manages an action menu for the application. A wants to let other components contribute actions to the menu in an easy and flexible way – for example by using XML files to describe these actions.

To be able to do this, component A should declare an extension point, let's name this point “actions”. Other components willing to contribute some actions to this "group effort" of a menu should use "actions" to indicate their contribution. (A component may declare any number of extension points and may contribute to any number of extensions to other components' extension points but we are using only point for this example.)

Components may declare extension points and extension contributions using a simple XML syntax like the following:

<?xml version="1.0"?>
<component name="A">
  <!-- A 'exposes' an extension point 'actions' that should be supplied with other components' MenuItem objects -->
  <implementation class="ImplA"/>	
  <extension-point name="actions">
    <object class="MenuItem"/>
  </extension-point>
  <!--A is 'contributing' a fragment of XML describing a 'doctype' to the extension point 'documentTypes' of B -->
  <extension target="B" point="documentTypes">
    <doctype name="File" extends="Document">
      <schema name="common"/>
      <schema name="file"/>
    </doctype>
  </extension
</component>

The content of an extension element is specific to the target extension point, such as the "doctype" in the example above. The extension element content is known only by the extension point. If a contribution to an extension point is made an the xml snippet is not correct from the standpoint of the component exposing the extension point, the result is unpredictable - generally, bad contributions will be ignored and some error will be logged. There is, for now, no mechanism of validating XML extensions like in Eclipse.

The Nuxeo Runtime provides an easy way to map XML extensions to real Java objects through an XML mapping mechanism called XMap. In the example above, the "doctype" can be thought of an object with a field called schema that contains a list of strings. Nuxeo supplies the XMap library to allow the XML fragment to automatically be transformed into a Java object with the fields correctly filled in. When not using XMap, extensions are returned as DOM elements and thus the component should itself perform the parsing of extension contributions.

For details on the XML mapping of XMap, see the XMap documentation and/or the JavaDoc.

XXX TODO ADD GRAPHIC

28.4.7.1. Use Cases

Here is the list of some use cases of the extension mechanism identified in the context of the Nuxeo ECM Platform:

  1. to define actions and menus

  2. to define content schemas (by importing XSD files)

  3. to define views (view ids mapped to JSF/XHTML pages)

  4. to define content objects (associate a Content Schema with a class that will provide required methods for the content object)

  5. to define permissions (usable in security annotations)

  6. to define PageFlows for Seam that, optionally, can extend existing ones

  7. to define business processes (in jBPM)

  8. to define content transformations (doc -> pdf, doc -> html, odf -> pdf, odf -> html, etc.)

  9. to define rules for the rule engine (that can be bound to some objects to run a rule only in a specific folder)

  10. scriptable extensions that define scripts binded to interpreters like JavaScript, Groovy, Jython, JRuby, etc

  11. to define JMS/Event queues

  12. to define event types

  13. to define security policies

  14. to define Access Control Policies

  15. to define NXCore storage backends (JCR, SQL, LDAP, etc.)

  16. to define query engines

  17. to define indexing engines

28.5. Supported Host Platforms

28.5.1. JBoss Integration

JBOSS permits its extensions to be packaged as SAR files, thus the Nuxeo Runtime provides a SAR package containing the Nuxeo Runtime and the JBoss OSGi adapter. This package is an OSGi bundle that acts as the OSGi system bundle. With the Nuxeo SAR in place inside JBoss, any packaging file format understood by JBoss (.sar, .jar, .ear, .war) or even a raw directory, will be treated as an OSGi bundle if it is found by JBoss and contains a valid OSGi manifest. In order for these bundles to be deployed, you need to have NXRuntime.sar already deployed in JBoss.

Besides the OSGi adapter and the auto-registration of components through bundle manifest, the JBoss adapter adds the capability to deploy runtime components as XML files located outside OSGi bundles through the JBoss deployment mechanism. This feature can be useful to register components that provide extensions to other components that can be described by plain XML without any code dependency.

Example of a plain XML component that contributes new document types:

<?xml version="1.0"?>
<component name="org.nuxeo.ecm.core.CoreExtensions">
  <extension target="org.nuxeo.ecm.core.schema.TypeService" point="doctype">
    <doctype name="File" extends="Document">
      <schema name="common"/>
      <schema name="file"/>
    </doctype>
    <doctype name="Folder" extends="Document">
      <schema name="common"/>
      <facet name="Folderish"/>
    </doctype>
    <doctype name="Workspace" extends="Document">
      <schema name="common"/>
      <facet name="Folderish"/>
    </doctype>
    <doctype name="Domain" extends="Document">
      <schema name="common"/>
      <facet name="Folderish"/>
    </doctype>
  </extension>
</component>
      

The NXRuntime.sar library offers two JMX services:

  1. The adapter service (nx:service=adapter)

    1. deploys OSGi bundles and declared components

    2. provides information about deployed bundles and components through the JBoss JMX Console

  2. The XML component deployer (nx:name=bundleDeployer,type=deployer) that can deploy XML descriptors as OSGi components

28.5.1.1. Installation

Deploy the NXRuntime.sar in JBoss, then deploy your OSGi bundles as common JBoss packages in any JBoss-supported format.

That's it,, your bundles are deployed and activated.

28.5.2. Eclipse Integration

For Eclipse, a NXRuntime.jar bundle is provided. Since Eclipse is OSGi-compliant, Nuxeo Runtime will not install any adapter (so it is not intervening on the bundle deployment).

When running on OSGi platforms, the main role of the runtime is to register components declared inside OSGi bundles (as seen previously through their manifest).

Because Eclipse is not starting automatically OSGi bundles (it starts them only on demand or on class loading), you need to update Eclipse's config.ini and configure it to start Nuxeo Runtime (i.e. org.nuxeo.runtime) when Eclipse starts:

osgi.bundles=org.eclipse.equinox.common@2:start, org.eclipse.update.configurator@3:start, org.eclipse.core.runtime@start,org.nuxeo.runtime@start

28.5.2.1. Installation

Update the config.ini file as described above, then copy NXRuntime.jar inside the Eclipse plugin directory.

Start Eclipse. You're done!

28.6. Using Nuxeo Runtime

28.6.1. Creating Components

Components may be created either from XML descriptor files, or programatically.

In order to register components you always need a runtime context.

28.6.1.1. Runtime Context

A runtime context is the context where a component is registered. Contexts are always associated with the bundle containing the component classes. Through the context, a component can access the runtime service and can load classes and retrieve resources from its bundle and other visible bundles. RuntimeContext objects depend on the current implementation of the runtime service, which varies based on the deployment environment:

Nuxeo Runtime provides three implementations of the RuntimeContext interface:

  1. org.nuxeo.runtime.model.impl.DefaultRuntimeContext: this is a simple implementation of a context designed to be used outside of an OSGi environment. This context uses the current thread context class loader. It is provided so that simple Java applications like JUnit tests can function properly without needing a full-blown OSGi system.

  2. org.nuxeo.runtime.osgi.OSGiRuntimeContext: this context can be used on any platform supporting OSGi bundles. This context uses the bundle's ClassLoader to load classes and find resources. This is the context supplied when the Nuxeo Runtime is deployed on a OSGi platform.

  3. org.nuxeo.runtime.jboss.JBossRuntimeContext: this is a JBoss specific context. This implementation wraps JBoss' DeploymentInfo object and uses the the JBoss infrastructure to load components deployed as standalone XML files.

Once you have a runtime context object, you can start registering components.

28.6.1.2. Creating components from XML descriptor files

To create a component using its XML description, follow these steps:

  1. Write the XML description of the component.

    Example of a simple XML descriptor:

    <?xml version="1.0"?>
    <component name="org.nuxeo.runtime.EventService">
      <implementation class="org.nuxeo.runtime.services.event.EventService"/>
      <extension-point name="listeners">
        <object class="org.nuxeo.runtime.services.event.ListenerDescriptor"/>
      </extension-point>
    </component>
    
  2. Load the XML file and register the component.

    In order to register a component, we always need a runtime context:

    // retrieve the current bundle
    Bundle bundle = ...
    // create a context given the current bundle object
    RuntimeContext context = new OSGiRuntimeContext(bundle);
    // load the component XML file given its location relative to the bundle root
    context.deploy(“OSGI-INF/MyComponent.xml”);

Note

The current bundle object is usually retrieved from a BundleActivator in the start(BundleContext context) method. You can also lookup other bundles by their symbolic names given a Bundle object.

The context has several method of deploying (e.g. installing) components. For example, the method used previously (Context.deploy(String)) is identical to:

// load the component XML file given its location relative to the bundle root
URL url = context.getLocalResource(“OSGI-INF/MyComponent.xml”);
if (url != null) {
    context.deploy(url);
}

28.6.1.3. Automatic deployment of components

The best and recommend way to deploy components is to let the infrastructure deploy them when the bundle is activated.

This can be done by specifying the local paths of the XML description files inside the bundle's META-INF/MANIFEST.MF file by using the Nuxeo-Component header as in the following example:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: HelloWorldExtension Plug-in
Bundle-SymbolicName: org.nuxeo.runtime.demo.HelloWorldExtension
Bundle-Version: 1.0.0
Bundle-Vendor: Nuxeo
Bundle-Localization: plugin
Require-Bundle: org.nuxeo.runtime.demo.HelloWorld
Nuxeo-Component: OSGI-INF/MyComponent.xml, OSGI-INF/MySecondComponent.xml

This way, as soon as the bundle is activated, theses two components defined in XML files, will be automatically deployed.

XML component are contained as resource files in that bundle and their path should be specified as relative to the bundle root with no initial slash. The two components' XML files shown in the previous example are located in the OSGI-INF directory at the root of the bundle's jar file.

28.6.1.4. Dependencies between components

Sometimes order of deployments between bundles or components is important and needs to be specified.

There are several ways to declare dependencies between them: it involves writing bundle names in the manifest file properties (Require-Bundle, Nuxeo-Require, Nuxeo-RequiredBy, Nuxeo-Component) and component names in contribution files (using the <require></require> tag).

For JBoss developers who do not care about deployment under eclipse: use "Nuxeo-Require" and forget about "Require-Bundle". Include all compile dependencies of your bundle to nuxeo components, and add other requirements if you'd like to make sure your bundle is deployed after another component. In other words, if bundle A lists bundle B on its Nuxeo-Require line, bundle B is deployed first.

For instance, if you'd like to make sure your bundle is deployed after nuxeo-core-api because you have compile dependencies on it and you would like to avoid class loading issue. You should use "Nuxeo-Require: org.nuxeo.ecm.core.api" in your manifest. If you'd like to make sure your bundle is deployed after nuxeo-core, perhaps because you need the default types, you can put "Nuxeo-Require: org.nuxeo.ecm.core".

Warning

Nuxeo-require should not be used between components that could be deployed on separate servers.

As a general rule for both properties, you should put all the compile dependencies of your bundle. This will prevent having any class loading issues. Dependencies are transitive: if you depend on nuxeo-core-api and on nuxeo-platform-types-api, you do not need to state nuxeo-core-api as types-api already depends on it.

For Require-Bundle, you may have to add "non-nuxeo bundles" requirements like apache commons-logging to make it work as expected under eclipse. This will obviously fail on jboss as this module in not seen as a bundle in this context: that's why you need to state your requirements in Nuxeo-Require instead.

"Nuxeo-RequiredBy" can be used as "Nuxeo-Require", except that it's the inverse dependency. It's useful when you need to put your bundle before another one. Plus it does not fail when the other bundle is not found, it may be a solution when needing to express dependencies between components that may be deployed on separate servers (although this should never happen if the api module is correctly done).

If your bundle has compile dependencies on jboss-seam, always add "Nuxeo-RequiredBy: org.nuxeo.ecm.war". The war module deployment triggers seam components detection so you need your bundle to be deployed before this is done, otherwise your seam components will not be detected correctly.

"Nuxeo-Component" is not designed to state dependencies, but as contributions are deployed in the given order, it can be helpful to express dependencies by modifying this order.

The <require></require> tag can be put in components. It is giving fine information about a component requirements. It can be used to control order when overriding another contribution. For instance, if you'd like to make sure your bundle is deployed after nuxeo-core types declarations, because you need to change default core types for instance, you can put <require>org.nuxeo.ecm.core.CoreExtensions<require> at the beginning of your core types contributions file. When contributing to an extension point, you do not need to express dependency to the component declaring the extension point: this requirement is implicit.

As a general note: usually, unless you're overriding an existing configuration, you do not need to order components as services that load them do not make much checks, especially when you're dealing with ordering od=f contributions within the same bundle. For instance, you do not need to make sure the "Folder" core type is deployed after the "dublincore" schema, as long as both are deployed when the repository is opened. Another example: you do not need to make sure the "file" layout is deployed before the "File" ecm type that references it, as long as both are deployed when the page displaying the document layout is loaded.

28.6.1.5. Creating components programmatically

This method of creating components is not recommended since it is internal to Nuxeo Runtime and it depends on the implementation.

Here is an example on how you can use the API to manually register a component. We assume you are running in an OSGi environment and you have a reference to the bundle object containing the component you want to register.

// retrieve the current bundle
Bundle bundle = ...
RegistrationInfoImpl ri = new RegistrationInfoImpl();
// create a context associated to the current bundle
ri.context = new OSGiRuntimeContext(bundle); 
ri.name = new ComponentName(“my.component”);
// set the class name of the component to register
ri.implementation= “org.nuxeo.runtime.example.MyComponent”;
// register the component
Framework.getRuntime().getComponentManager().register(ri);

28.6.2. Using components

28.6.2.1. Responding to life cycle events

When a component is deployed, Nuxeo Runtime will check its dependencies and if all of them are resolved, the component is resolved and activated. (In the future, lazy activation or activation on demand will be supported too). If component dependencies are not satisfied, the component will be put in a pending queue until all of its dependencies are resolved.

When activating a component, the runtime will check if the component defines the activate life cycle method and if true, it will call it to get a chance to the component to initialize itself.

The same thing is done when deactivating the component - the runtime will check if the component defines the deactivate life cycle method and if true, it will call it to give a chance for the component to dispose itself properly.

There are two ways to define life cycle methods:

  1. By implementing the org.nuxeo.runtime.model.Component interface

    In this case, a cast to Component interface is performed and the life cycle methods are called.

    public interface Component extends Extensible {
        public void activate(RuntimeContext context) throws Exception;
        public void deactivate(RuntimeContext context) throws Exception;
    }
  2. By simply declaring a public or protected methods on the component object using the right signature.

    In this case the Java reflection mechanism is used to call the methods.

    public class MyComponent {
        ...
        public void activate(RuntimeContext context) throws Exception {
            ...
        }
        public void deactivate(RuntimeContext context) throws Exception {
            ...
        }
       ...
    }

28.6.2.2. Looking up components

After a component is activated, it can be retrieved using the Nuxeo Runtime API.

There are several methods to look-up a component:

  • Looking up the component by its name:

    HelloComponent hc = (HelloComponent) Framework.getRuntime().getComponent(
            "org.nuxeo.runtime.demo.HelloComponent");
  • Looking up the ComponentInstance object corresponding to this component. This object is a proxy to the component object:

    ComponentInstance ci = Framework.getRuntime().getComponentInstance(
            "org.nuxeo.runtime.demo.HelloComponent");
    if (ci != null) {
        HelloComponent hc = (HelloComponent) ci.getInstance();
    }

28.6.2.3. Working with extension points

Now let's take a look at how a component may define an extension point and how other components may use this extension point to contribute extensions.

Defining an extension point

A component may define any number of extension points. Extension points are identified inside a component by a unique name. We will describe here how to define extensions using the XML descriptor file. Extension points can also by created by hand using the internal API of Nuxeo Runtime but this is no recommended and it is not documented here.

Extension points are specified in the XML component descriptor using the extension-point tag. This tag has a required attribute name and one or more optional object sub-tags.

  1. The name attribute.

    This should be unique relative to the parent component and is used to identify the extension points inside a component.

  2. The object sub-tag can be used to define what kind of objects are contributed by XML extensions. These objects will be created from the extension XML fragment by using the XMap engine that maps XML to Java objects through through Java annotations.

    If no object sub-tag is specified, the extension will be contributed as a DOM element.

    The object tag has a required class attribute that specifies the class name of the objects to contribute.

    The object class will be loaded using the context of the bundle that defined the extension point.

Example of a component declaring two extension points:

  1. listeners

  2. asyncListeners

    <?xml version="1.0"?>
    <component name="org.nuxeo.runtime.EventService">
      <implementation class="org.nuxeo.runtime.services.event.EventService"/>
      <extension-point name="listeners">
        <object class="org.nuxeo.runtime.services.event.ListenerDescriptor"/>
      </extension-point>
      <extension-point name="asyncListeners">
        <object class="org.nuxeo.runtime.services.event.AsyncListenerDescriptor"/>
      </extension-point>
    </component>
Contributing an extension

Once a component declaring some extension points has been activated, other components may contribute extensions to that extension point.

To declare an extension, the extension tag is used. This tag must contains a target and a point attribute.

  1. target

    The target attribute specifies the name of the component providing the extension point

  2. point

    The point attribute is the extension point name.

The extension element may contain arbitrary XML. The actual XML content is recognized only by the extension point to where the extension is contributed. This means you should know the correct format for the extension XML.

For this reason, it is important for components to document their extension points. If the extension point is using XMap to map XML to Java objects, then you can use annotations existing on the contribution object class to know the XML format. These annotations are easy to understand and can be used as well as a documentation for the XML extension format.

If you are familiar with Eclipse extension points, you may wonder why Nuxeo Runtime is not using an XSD schema to define the content of an XML extensions. The reason is simple: because inside our ECM project we need to be able to define any type of XML content - even configuration files from external tools we use like for example a Jackrabbit repository configuration. Defining and maintaining XSD schemas for this kind of extensions would be painful.

Anyway, using XMap to map extensions to real Java objects makes it easy to use extensions.

Here is an example on how a component is declaring some contributions to the previously defined extension points:

<?xml version="1.0"?>
<component name="my.component">
  <implementation class=”MyComponent"/>
  <extension target="org.nuxeo.runtime.EventService" point="listeners">
    <listener class="org.nuxeo.runtime.jboss.RepositoryAdapter">
      <topic>repository</topic>
    </listener>
    <listener class="org.nuxeo.runtime.jboss.ServiceAdapter">
      <topic>service</topic>
    </listener>
  </extension>
</component>

You can see how the component is declaring an extension to the listeners extension point defined by the component org.nuxeo.runtime.EventService

The result of this declaration is that the EventService will register two listeners, one listening on events from the “repository” topic, the other on events from the “service” topic.

Registering contributed extension

Extensions are contributed to the target extension point immediately after the component declaring these extensions is activated. If the target component (the component declaring the extension point) was not yet activated, the contributed extensions are put in a pending queue and they will be contributed as soon as the target component is activated.

A component willing to declare extension points and accept contributed extensions should declare two protected or public methods: registerExtension and unregisterExtension.

This can be done either by implementing the Component interface, or by declaring these methods with their correct signatures on the component object (as we have seen before for the life cycle methods).

These two methods should have the following signature:

public interface Extensible {

    public void registerExtension(Extension extension) throws Exception;

    public void unregisterExtension(Extension extension) throws Exception;

}

Note that the Extensible interface is extended by the Component interface.

When an extension is contributed the registerExtension method is called with an argument that points to the actual contributed extension as an Extension object.

Components should use this method to do something with the extension (usually to register it somewhere).

When the component contributing the extension is deactivated, the Runtime will call the unregisterExtension method using the same Extension object as a parameter. This gives a chance to the extended component to unregister extensions when they become inactive.

Here is an example of how extensions are registered and unregistered:

public class HelloComponent implements Component {
    public final static ComponentName NAME 
        = new ComponentName("org.nuxeo.runtime.demo.HelloComponent");

    Collection<HelloMessage> messages = new ArrayList<HelloMessage>();
    
    public void registerExtension(Extension extension) throws Exception {
        Object[] messages = extension.getContributions();
        for (Object message: messages) {
            HelloMessage msg = (HelloMessage)message;
            this.messages.add(msg);
            System.out.println("Registering message: " + msg.getMessage());
        }
    }

    public void unregisterExtension(Extension extension) throws Exception {
        Object[] messages = extension.getContributions();
        for (Object message: messages) {
            HelloMessage msg = (HelloMessage)message;
            this.messages.remove(msg);
            System.out.println("Un-Registering message: " + msg.getMessage());
        }
    }
...
}

You can see how the contributed objects are fetched from the Extension object and then registered into a Java Map. These contributions are objects of type HelloMessage as defined by the extension point (using the object sub-element)

The contributions are also available as a DOM element so you can use this to retrieve contributions in the case you don't use XMap to map XML extensions to Java objects. This DOM element is corresponding to the extension element from the XML component descriptor.

So if you need to retrieve the DOM representation of the extension, you can do:

public void registerExtension(Extension extension) throws Exception {
    Element element = extension.getElement();

    // parse yourself the DOM element and extract extension data
    ...
}

28.6.3. XML Component Descriptors

In this section we will describe the most important elements composing an XML component descriptor.

You can inspect the XMap annotations on the class org.nuxeo.runtime.model.impl.RegistrationInfoImpl to find all elements that may compose an XML component descriptor.

A complete component descriptor may look like this:

<?xml version="1.0"?>
<component name="org.nuxeo.ecm.core.schema.TypeService">
    <implementation class="org.nuxeo.ecm.core.schema.TypeService"/>

    <require>org.nuxeo.ecm.core.api.ServerService</require>
    <require>org.nuxeo.ecm.core.repository.RepositoryService</require>

    <property name="author">Bogdan Stefanescu</property>
    <property name="description">The component description ...</property>

    <extension-point name="doctype">
        <object class="org.nuxeo.ecm.core.schema.DocumentTypeDescriptor"/>
    </extension-point>
    <extension-point name="schema">
        <object class="org.nuxeo.ecm.core.schema.SchemaBindingDescriptor"/>
    </extension-point>

    <extension target="org.nuxeo.ecm.core.api.ServerService"
               point="clientFactory">
        <factory class="org.nuxeo.ecm.core.api.impl.LocalClientFactory"/>
    </extension>
    <extension target="org.nuxeo.ecm.core.schema.TypeService" point="schema">
        <schema name="common" src="schema/common.xsd"/>
        <schema name="core-types" src="schema/core-types.xsd"/>
        <schema name="file" src="schema/file.xsd"/>
    </extension>
</component>

Each component is defined inside its own file. As you can see the root element is component. This element has a required attribute name. Apart this, all other sub-elements are optional.

Here is a list with all supported sub-elements:

  1. implementation

    This element is used to specify the component implementation class. The element is not required since one may define plain XML components only for contributing some extensions to other components. We will refer to these components as extension components.

  2. require

    This element can be used to specify dependencies on other components. The component will be resolved and activated only after all these dependency are resolved.

  3. property

    This element can be used to define random properties that will be available later to the component when it will be created.

  4. extension-point

    This element is used to declare extension points. A component may declare any number of extension points.

    See more details on this in Working with extension points section.

  5. extension

    This element can be used to declare extensions to other components (or to the current component itself).

    See more details on this in Working with extension points section.

28.7. Integration tests for Nuxeo Runtime applications

While it's obviously a good thing to unit-test one's code, it's usually not enough for a module designed to be ran as part of a Nuxeo Runtime application. It's indeed likely in this situation that the module will depend on services provided by other modules, and even maybe on their default configuration. It's even not uncommon that a project-specific module consists almost exclusively of calls to generic services provided by the base software platform.

28.7.1. The NXRuntimeTestCase base class

org.nuxeo.runtime.test.NXRuntimeTestCase is a base class to write JUnit tests for Nuxeo Runtime applications. It sets up the Nuxeo Runtime environment (in the setUp method) and provides methods to control bundle and resources loading. It is designed to behave in the same manner in Maven and Eclipse situations. Therefore resources must be accessed in a way that does not depend on the actual ordering of classpath.

28.7.1.1. Loading a bundle

To load a whole OSGI bundle, use the deployBundle method, whose parameter is the bundle symbolic name, as specified in its manifest.

28.7.1.2. Loading one contribution from a bundle

Loading a whole bundle can be too heavy, or bring unwanted default configurations. Therefore, the deployContrib method is provided to load just a resource (service definition, extension point contribution, etc.) from a given bundle. It takes two arguments: the bundle symbolic name, and the path to the contrib from the top of bundle.

28.7.1.3. Loading a test resource

For resources from the test packages, just make an OSGI bundle of the test package, which can be done by creating the META-INF/MANIFEST.MF at the top of the target jar, and use deployContrib as above.

28.7.1.4. Sample usage

The following is an excerpt from org.nuxeo.project.sample:

public class TestBookTitleService extends NXRuntimeTestCase {

    private BookTitleService service;

    private static final String OSGI_BUNDLE_NAME = "org.nuxeo.project.sample";

    private static final String OSGI_TEST_BUNDLE = "org.nuxeo.project.sample.tests";

    public void setUp() throws Exception {
        super.setUp();
        // deployment of the whole nuxeo-project sample bundle
        deployBundle(OSGI_BUNDLE_NAME);

        service = Framework.getService(BookTitleService.class);
    }

    public void testServiceContribution() throws Exception {
        // Lookup is ensured simply by making the 'test' sub-hierarchy a
        // bundle of its own, with a MANIFEST file
        deployContrib(OSGI_TEST_BUNDLE, "sample-booktitle-test.xml");
        assertEquals("FOOBAR Test", service.correctTitle("foobar"));
    }

}

28.7.2. Frequent patterns

While working on an integration test, it is always worthwhile to prescribe clearly what is to be tested: either API calls to services provided by other modules, consistency with configuration provided by other modules, default configuration for the module being tested. This is especially important for the non-regression aspect of the testing and the cost of maintaining the tests.

The use-cases discussed below require basic knowledge of the Nuxeo ECM framework. Implicitely, it is somehow assumed that the tested code has to interact with one service. In case of multiple target services, one would have to choose a pattern for each of them.

28.7.2.1. Integration test against base services with testing configuration

We want to check that the API calls from the tested component to other components have the desired effect, but we don't want to rewrite the tests each time the default configuration of the other components change. Typically, this means that we need to deploy the xml contributions that define the services we need, together with the minimal configuration to tie it up together.

Example: the search service is able to configure its indexes automatically from the schemas and core types declaration. We don't want to have to update tests if someone changes the default config that ships with nuxeo-core. Ideally, this test should use deployBundle to set up core services, test repository, etc. and then work on dedicated schemas and core types that are loaded by deployContrib.

28.7.2.2. Integration test against base services and their default configuration

One can imagine here a core event listener that uses a given schema, a component that needs access to the search service to manipulate some specific documents...

In this case, we need to load the base service and its configuration exactly as they are in the real application and we do want the test to catch errors that are due to a change in said configuration. In this pattern, we'd use deployBundle all over the place.

It's likely however that one does not want the test to rely on the default (if any) configuration of the module being tested. If the tested component doesn't carry its configuration but still needs to be deployed within Nuxeo Runtime, deployBundle can be used on itself, and then deployContrib for the test configuration, after the test package has been upgraded to an OSGI bundle.

Variant: testing of a component and the configuration that comes along. Just think of your tested module as a "base service."

28.7.2.3. Reusing test resources from another component

This is usually neither possible nor recommended. Such situations do appear in the Nuxeo code base, and the proper solution is to provide the wished resources or classes from a package, precisely like org.nuxeo.ecm.runtime.test does.

28.8. Detailed Architecture

28.9. References

  1. Nuxeo.org website: http://www.nuxeo.org/

  2. OSGi website: http://www.osgi.org/

  3. JSR 277, 291 and OSGi, Oh My! - OSGi and Java Modularity, presented by Richard S. Hall at ApacheCon Europe 2006: http://docs.safehaus.org/download/attachments/2995/osgi-apachecon-20060628.pdf