Skip to content
bstefanescu edited this page May 6, 2011 · 11 revisions

What is the Eclipse Enterprise Content Repository (ECR)?

ECR is a platform to build rich content applications. You can use ECR to develop any type of application that need to manage structured (or unstructured) content and expose this content to the world using the interface of your choice (web sites, REST APIs, CMIS, or native Java API for embeddable applications).

ECR is providing a rich set of services on top of the content storage like versioning, access control, REST bindings and also an easy way to create web sites or REST services to expose your content.

Concepts

Extension Points

ECR is a very flexible platform composed from many bundles that may contribute one to each other configuration objects, service implementations or other type of configuration. This modularity is done by using an extension point mechanism. Extension points are managed by a core service provided by org.eclipse.ecr.runtime bundle.

How extension points work? I will take an example to explain the need of extension points. Let say you are developing a web page that display documents from the repository (documents are objects stored in the repository). You may want to add a toolbar that provide some common actions like 'Delete' or 'Create' to delete the displayed document or to create a new document as a children of the current document. But you may want to also provide some actions only in certain conditions. For example you may want to provide an 'Export to PDF' action but only if a PDF exporter is installed in the application. As you are developing an OSGi application, services (like the PDF Exporter) may be installed or removed from the application at runtime. But you need to correctly update your web page to show or hide the 'Export to PDF' action when the PDF exporter service became available or is removed.

The ECR approach of handling this sort of dynamic configuration are extension points.

Thus, to implement our hypothetical toolbar we need to define an extension point on the toolbar service which is accepting contribution from outside as 'action' objects. The bundle providing the PDF Exporter service will declare a new 'action' contribution to your extension point, so that when the bundle is activated your extension point will be notified about the new available action, and it will be able to display the new action when you enter the web page. The same, when the bundle is uninstalled the 'action' contribution will be removed from the extension point and the web page will no more display the action.

So, an extension point is in fact a registry of a given type of contributions. The extension point is tracking bundles in the application to register or remove contributions when bundles become available or are removed. The extension point is materialized by a Java class which manage the contribution registration and removal and the contribution is materialized by a Java class which is describing the object to contribute.

In ECR, extension points and contributions are declared using XML files. Each time an XML file containing contributions is found the contribution objects will be instantiated from their corresponding XML definition.

When you want to declare one or more extension points for a service you need to create an XML file where you define the extension point manager class and the contribution types each extension point is accepting.

Declaring an extension point

<?xml version="1.0"?>
<component name="org.eclipse.ecr.core.schema.TypeService"
  version="1.0.0">
  <implementation class="org.eclipse.ecr.core.schema.TypeService" />
  <extension-point name="schema">
    <object
      class="org.eclipse.ecr.core.schema.SchemaBindingDescriptor" />
  </extension-point>
</component>

In this example we are declaring a new component that provide an extension point. The component implementation class is managing the contribution registry and must implement the org.eclipse.ecr.runtime.model.Component interface or extend the org.eclipse.ecr.runtime.model.DefaultComponent class.

You can see that the TypeService component is declaring an extension point named schema which is accepting contributions of the type org.eclipse.ecr.core.schema.SchemaBindingDescriptor.

The component XML file should be placed somewhere in the bundle and need to be declared in the MANIFEST.MF so that it will be discovered at runtime. Let's say you put the component XML file in OSGI-INF/type-service.xml. Then in the MANIFEST.MF you should add an entry like this:

Nuxeo-Component: OSGI-INF/type-service.xml

Declaring extensions (i.e. contributions to an extension point)

To contribute a new schema definition to the schema extension point you need to create a component XML file that define the contributions:

<?xml version="1.0"?>
<component name="org.eclipse.ecr.defaultSchemaContributions"
  version="1.0.0">
  <extension target="org.eclipse.ecr.core.schema.TypeService"
      point="schema">
    <schema name="common" src="schema/common.xsd"/>
    <schema name="dublincore" prefix="dc" src="schema/dublincore.xsd"/>
    <schema name="file" src="schema/file.xsd"/>
  </extension>
</component>

As for the extension points definition you need to put this file somewhere in the bundle and reference it using the Nuxeo-Component property in the bundle MANIFEST.MF.

You can see that we are declaring 3 new schemas named common, dublincore and file, each of these schemas being defined by the XSD file referenced by the src attribute.

You can see that the extension element is declaring a target component and an extension point name. This is to indicate where to contribute the extensions - to which component and on which extension point declared by the target component.

Notes

  • A component may declare multiple extension points, each extension point being identified by a name and one or more types of contribution objects.
  • A component XML file can contain both extension point and contribution definitions.
  • XML component files that declares only contributions are not required to provide an implementation for the component (since they are not handling any extension point).

XML Extension mapping

As we saw before the schema extension point from the TypeService component is accepting contributions of type org.eclipse.ecr.core.schema.SchemaBindingDescriptor. But when declaring extensions to that extension point we are simply providing some XML elements not real Java objects. What is the magic? How these XML elements are transformed into real Java objects?

The response is that contribution objects declared by extension points are annotated to provide an XML mapping. Here is the org.eclipse.ecr.core.schema.SchemaBindingDescriptor class:

@XObject("schema")
public class SchemaBindingDescriptor {

    @XNode("@name")
    public String name;

    @XNode("@src")
    public String src;

    @XNode("@prefix")
    public String prefix = "";

    @XNode("@override")
    public boolean override = false;
    
    ...
}

You can see that the @XObject annotation is binding the XML element named schema to an instance of this class. Each schema element attribute is bound to an object field using @XNode annotations.

The mapping mechanism allows you to bind XML attributes or elements on fields or setter methods. There are several types of fields supported:

  • Any Java primitives like: int, boolean, double etc.
  • Any primitive objects like: String, Integer, Boolean, Class, URL etc
  • org.w3c.dom.DocumentFragment objects to bind XML sub-trees
  • Any other objects annotated with @XObject to bind complex objects
  • Arrays or Collection of bindable objects - using @XNodeList annotation
  • Maps with string keys to bindable objects - using the @XNodeMap annotation

The extensions context - looking up classes and resources.

It is common to have extensions that references in the extension XML Java classes or other type of resources. This is an important aspect because the target extension point must be able to locate these resources somewhere in the application. The usual mechanism of looking up resources is to use the bundle that contributed the extension to find the resource (using Bundle.getEntry() or Bundle.loadClass() for example). This is the responsibility of the component providing the extension points to correctly lookup the resources. Each contributed extension has a contribution context which can be used to retrieve the bundle that declared the extension.

Note the XML mapping mechanism already provides some support for automatic Java class lookup. If you need to inject a class in a contribution object just use the Class type for your field as in the following example:

@XObject("factory")
public class Factory {
  
  private @XNode("@class") Class<?> clazz;

  public Object getInstance() throws Exception {
     return clazz.newInstance();
  }
}

By using the Class<?> type for you injected field the string value of the corresponding XML attribute will be automatically transformed into a Class object by using the Bundle.loadClass() method of the bundle that contributed the extension.

Global configuration

There are situation when you don't want to package your XML components used to declare extensions in a bundle. This is the case for example if you want to configure some global feature in a configuration directory so that you can easily modify the configuration by hand to change the behavior of your application without needing to modify the extension files in bundles repackaging them and then reinstalling them.

ECR is allowing you to use such configuration directories where you can put "global" extensions. This is very useful for example to declare servlets that are always available in the application or for example to configure the connection to a postgresql database etc. It make no sense to put such configuration inside bundles. You put configuration in bundles only if you want to contribute something when that bundle becomes available in the application - but for general configuration you may want to use XML files outside any bundle.

This sounds fine, but there is a problem related to the context of the extensions that are contributed from outside bundles. As ECR is an OSGi application - each bundle has its own class loader. Because the XML extension may reference a class name - the extension point will not be able to find the class if the extensions is not in a context of a bundle.

ECR provide a way to overcome this by allowing XML extension to declare from which bundle it belong. Example:

<?xml version="1.0"?>
<component name="org.eclipse.ecr.platform.webConfig" version="1.0" bundle="org.eclipse.ecr.web.jaxrs">
  <extension target="org.eclipse.ecr.web.jaxrs.servlet.config.ServletRegistryComponent" point="servlets">
    <servlet name="jaxrs" path="${ecr.web.root}" class="org.eclipse.ecr.web.jaxrs.servlet.ApplicationServlet">
  </extension>
</component>

You can see the XML extension above is contributing a servlet org.eclipse.ecr.web.jaxrs.servlet.ApplicationServlet from a global extension file which is not located in a bundle. By using the attribute bundle="org.eclipse.ecr.web.jaxrs" the extension will be assumed to be part of the org.eclipse.ecr.web.jaxrs bundle - and thus the class name will be resolved using that bundle context.

Providing Services

Document Types

Clone this wiki locally