Skip to content
sfermigier edited this page May 11, 2011 · 4 revisions

As we saw in the Extension Points section, components are singleton objects that are registered when a bundle declaring an XML component becomes available.

These objects may be used to achieve a variety of tasks like:

  1. Being notified when the bundle declaring the component is resovled
  2. Being notified when the bundle declaring the component is unresolved
  3. Being notified when the ECR application was started
  4. Providing services. See Services section
  5. Managing extension points. See Extension Points

A component may look like a sort of Bundle Activator. Both are managed singleton objects activated and deactivated during changes in the bundle's life cycle. But there are also differences:

  • You can have any number of components in a bundle
  • Components are defined through an XML descriptor file
  • Components are mainly used to wire your bundles using ECR extension points

Declaring components

As we've seen in the Extension Points section, to declare a component you should create an XML file, put it somewhere in the bundle, and reference that file inside the MANIFEST.MF using the Nuxeo-Component header.

Here is an example of an empty component XML file:

<?xml version="1.0"?>
<component name="org.eclipse.ecr.sample.EmptyComponent"
  version="1.0.0">
</component>

To declare this component, put the file, let's say, in OSGI-INF/my-empty-component.xml inside your bundle and add the following header in your MANIFEST.MF:

Nuxeo-Component: OSGI-INF/my-empty-component.xml

You can declare any number of component in your bundle by referencing them all in the Nuxeo-Component header. Example:

Nuxeo-Component: OSGI-INF/my-empty-component.xml, OSGI-INF/my-second-component.xml

Usually, apart the XML descriptor file, components have an Java implementation object that implements the org.eclipse.ecr.runtime.model.Component interface.

Although, you can directly implement this interface it is recommended to extends the class org.eclipse.ecr.runtime.model.DefaultComponent which have a richer set of methods.

But, a component may not necessarily provide an implementation class. We will discuss this special case in the next section.

Configuration Components

Configuration component are a special case of components that are used only to provide extensions to existing extension points in the application. This type of components doesn't need to provide an Java implementation for the component.

Example:

<?xml version="1.0"?>
<component name="org.eclipse.ecr.core.api.blohodlers.adapters">
  <extension target="org.eclipse.ecr.core.api.DocumentAdapterService"
    point="adapters">
    <adapter class="org.eclipse.ecr.core.api.blobholder.BlobHolder"
      factory="org.eclipse.ecr.core.api.blobholder.BlobHolderAdapterFactory" />
  </extension>
</component>

You can see that this component is only contributing an extension to DocumentAdapterService through the adapters extension point.

Writing a component class

We will talk now about regular components - that also provides a Java implementation class.

These components are useful to hook into the application (e.g. to be notified about application startup, etc) or to provide services or manage extension points.

This type of component may declare any number of extension points, services or contributions.

Here is a minimal component declaration:

<?xml version="1.0"?>
<component name="org.eclipse.ecr.sample.MyComponent">
  <implementation class="org.eclipse.ecr.sample.MyComponent" />
</component>

This XML component descriptor declares a component implemented by the org.eclipse.ecr.sample.MyComponent class.

Here is an example of a simple component implementation

public class MyComponent extends DefaultComponent {
    @Override
    public void activate(ComponentContext context) throws Exception {
        System.out.println("Activating component "+MyComponent.class);
    }
    @Override
    public void deactivate(ComponentContext context) throws Exception {
        System.out.println("Deactivating component "+MyComponent.class);
    }
    @Override
    public void applicationStarted(ComponentContext context) throws Exception {
        System.out.println("Application started");
    }
    @Override
    public <T> T getAdapter(Class<T> adapter) {
        return null;
    }
    @Override
    public void registerContribution(Object contribution,
            String extensionPoint, ComponentInstance contributor)
            throws Exception {
    }
    @Override
    public void unregisterContribution(Object contribution,
            String extensionPoint, ComponentInstance contributor)
            throws Exception {
    }
}
  1. The activate method

    This method should be used to initialize the component and will be invoked by the component manager when the component is put into service.

    It takes as argument a ComponentContext which can be used to retrieve the BundleContext and other useful context data.

    You can use it for example to instantiate the services you want to export.

  2. The deactivate method

    This method should be used to cleanup any resource held by the component and will be called by the component manager before the component is removed.

  3. The applicationStarted method

    This method will be called by the component manager after the ECR Application started (that means that all components found at startup were activated and all extension point wiring is done).

  4. The getAdapter method

    This method is called to get an instance of the services declared by this component.

    For more details see the Services section.

  5. The registerContribution method

    This method will be called for each contribution that is contributed to one of the extension points declared by this component.

    It takes as arguments the contribution object, the name of the target extension point and the context of the contributor. (You can use this context to get for example the Bundle of the contributor).

    For more details see the Extension Points section.

  6. The unregisterContribution method

    This method will be called for each contribution that should be removed from one of the extension points declared by this component.

    It takes as arguments the contribution object, the name of the target extension point and the context of the contributor. (You can use this context to get for example the Bundle of the contributor).

    For more details see the Extension Points section.

Component life cycle

Let's look now on how components are activated and when the notification methods we described above are called by the component manager.

When the org.eclipse.ecr.runtime bundle starts (the bundle activator is started) it will open a bundle tracker to track any resolved bundle declaring components.

At this point all existing resolved bundles are scanned for components.

For each component which was found a new component instance is created (if the component provide an implementation) and the component is activated (by calling the activate method).

Thus components are activated when the bundle are at least in the RESOLVED state. This means that on OSGi platforms supporting lazy loading, bundles which use lazy activation policy will be automatically STARTED by the component instantiation.

In that case, the component activate method will always be called after the start method of the bundle activator was called (if any activator is defined).

After a component is activated all the extensions declared by the component will be contributed to the target extension points, and all the pending extensions contributed to the extension points provided by the component will be registered (i.e. the registerContribution method will be called).

After all detected components are activated (and extension point wiring is completed) the applicationStarted method is called on each component.

While te application is running for each newly resolved or unresolved bundle that contains ECR components will trigger the activation/deactivation of the component.

If a component containing extension points is deactivated - all the existing contribution to that extension points will be put into a pending queue - this way if the component will be activated again it will register again all pending contributions.

Next: Go to the Services section for details on how components may provide services.