Chapter 4. OSGi Bundles, Components & Extension Points [DRAFT]

Table of Contents

4.1. This is a DRAFT! Give your opinion!
4.2. OSGi background
4.3. Components and their life cycle
4.4. Interacting with other components
4.4.1. What exactly is "Nuxeo?"
4.5. Extension Points
4.6. Bundles, components, and extension points in action
4.7. The big picture

Objective: To explain the basic principles and practice behind OSGi, component-based software development, and the use of extension points to add new functionality to an existing system.

The wheel is an extension of the foot, the book is an extension of the eye; clothing, an extension of the skin, electric circuitry, an extension of the central nervous system. H. Marshall McLuhan, Canadian communications theorist (1910-1980).

4.1. This is a DRAFT! Give your opinion!

If you have any comments, questions, or general-purpose harassment you would like give us about this book, then please use the comment form at the bottom of each page! We promise that we will try to incorporate any feedback you give (minus the profanity, of course), will respond to your questions, and credit you appropriately.

4.2. OSGi background

OSGi, née 1999 as the Open Services Gateway Initiative, is many things to many people. A full discussion of the history of, viewpoints on, and capabilities of OSGi is beyond the scope of this book and would likely fill an entire volume on its own! We encourage readers who are interested in OSGi's origins and rationale to consult http://en.wikipedia.org/wiki/OSGi and http://www.osgi.org/Main/HomePage for more detailed information as well as http://osgilook.com/ for recent information about tutorials and books. This book will adopt the utilitarian approach of only discussing OSGi as it relates to developing software for the Nuxeo platform. As of January 2009, Nuxeo uses version 4.1 of the OSGi standard, also known as JSR-291 within the Java Community Process.

4.3. Components and their life cycle

At its core, the OSGi "model" is a way of managing components or, more colloquially, "blobs of code." [Definition: A component in OSGi is a set of related Java classes that should be kept together and managed as a unit]. For example, if we create a component called org.nuxeo.foo made of several Java classes, an OSGi container will manage these classes as one entity. [Definition: An OSGi container is a program that manages components, usually many simultaneously, and enforces rules about the way that components can behave and interact with each other.] A container, for example, is responsible for loading a component from disk-where the component is just a lifeless set of Java classes with some configuration information-into memory and starting it running.

As a developer of functionality or applications based on Nuxeo, your goal will be create a bundle.[Definition: A bundle, in Nuxeo-OSGi parlance, is a single file that contains all the information about a component]; this single file is in "jar" format, nearly identical to the "jar archives" that Java developers use to distribute libraries of code. We can now start an example: the org.nuxeo.foo logical unit of functionality, is contained in a bundle, and stored on disk as a jar file, probably with the filename something like nuxeo-foo.jar. This file contains all the compiled Java .class files needed for the functionality of foo, plus some extra files like a MANIFEST.MF. When the container loads the bundle nuxeo-foo.jar to begin using the component, it will read the MANIFEST.MF file to get any special instructions that the bundle's author had for the container about this bundle. Then the container will try to find a special class, designated in the manifest file, and send that special class a life cycle message, such as start().

[Definition: The life cycle of a component is the set of states that it passes through during its execution.] Managing the life cycle of its components is a critical part of a container's job. As the life cycle proceeds, the container tells the component status information by calling methods on a class in the container. As stated before, one of these is start() and there, obviously, is a stop() that indicates the component is being shutdown and will soon be removed from the container. There are a number of other parts to the life cycle, but these can be be ignored for now. When you write a "program" that "runs on Nuxeo" it can be more precisely stated that you have a component that can be successfully loaded by the Nuxeo container and that passes through the life cycle of a component; during some of these life cycle phases your code will be running and can take whatever actions you can devise for a Java program.

4.4. Interacting with other components

As we stated previously, an OSGi container typically manages many components at once, and Nuxeo is no exception. By using facilities provided by the container one component, like org.nuxeo.foo, can find another component, say org.nuxeo.bar, so that functionality exposed by bar can be consumed by foo. All of these interactions, both with the container, and between the two components, are implemented either as Java code or in configuration files provided in the component's bundle. All of the interactions between components are mediated through the container to enforce security restrictions, or any other policy choices for that matter, that the container decides.

Let's look at a fictional MANIFEST.MF file that might be packaged into the bundle nuxeo-foo.jar to tell the Nuxeo container some critical information. This is not a complete manifest file, but should help you get a sense of what information the container and bundle might exchange.

Administrator

#
# The manifest is located in the jar file of the bundle at META-INF/MANIFEST.MF
#
Bundle-Version: 5.2.0
Bundle-Name: nuxeo's foo project 
Bundle-SymbolicName: org.nuxeo.foo 
Nuxeo-Require: org.nuxeo.bar 

If you look at this manifest file, you will see some critical bits of information for a container to know about our component in Key:Value form; lines that start with # are comments. First, you will note the bundle's version is set to "5.2.0". This is more important than one might initially think, because many OSGi containers can handle having multiple, different versions of a bundle loaded simultaneously! The different versions are distinguished by this version information. You can also see two versions of the "name" of the bundle. The first of these is just for human consumption and can be any text. The latter name, SymbolicName, is the name that other components will use to refer to this bundle. It is customary, although not required, that we use the Java "reverse domain" convention for naming a component and we will follow that convention throughout this book.

Finally, the most interesting line is the last one. This line tells the container that the bundle and component referred to by this manifest file cannot function properly without access to the bundle org.nuxeo.bar. Obviously, the container will take some action, such as searching through its already loaded bundles or perhaps certain directories in the file system, to see if the bundle org.nuxeo.bar can be located. The target bundle must itself have a manifest file with the line Bundle-SymbolicName: org.nuxeo.bar so that it can recognized properly! If the target bundle cannot be located, then foo will be rejected and will not proceed through the normal life cycle. As you will see in future lessons, this ability to combine functionality from many bundles is a key reason to use the OSGi model and construct an application in this component-oriented way.

4.4.1. What exactly is "Nuxeo?"

If you have been paying careful attention in this lesson, you will have noticed that we have been deliberately imprecise about exactly what role "Nuxeo" plays with respect to OSGi. To clarify, the Nuxeo 5.2 server is a subset OSGi container. By subset, we mean that the Nuxeo container does not fully implement the OSGi specification for containers; it implements only enough of the OSGi specification to load, manage, and execute components in an "OSGi-like" way.

However, we often use the word "platform" to describe Nuxeo, it's the 'P' in Nuxeo 5 EP! "Platform" is used because the code you get when you download Nuxeo 5 EP includes hundreds of components that have been written by the Nuxeo development team. These components perform many useful functions such as indexing documents, searching for documents based on query terms, and managing user accounts. These components are well-formed with respect to the OSGi specifications and will run in OSGi compliant containers. We know this is true because we've tried it! The reason for this difference in terms of conformance to the specification is that there are a number of open-source OSGi containers available (see sidebar) so it is unecessary for Nuxeo to provide a "real" OSGi server. It is also in the future plan to ship our components bundled with a different, fully-compliant OSGi container, such as Glassfish, avoiding our container entirely if you need a true OSGi environment.

So, to be strictly correct, we should be careful to distinguish when we are referring to the Nuxeo OSGi container as opposed to one of the legion of the Nuxeo components that are downloaded with the container. However, the noun "Nuxeo" has entered common usage to mean either or both of these parts of the software and we will continue this unfortunate tradition in cases where it is not too confusing; c'est la vie.

4.5. Extension Points

Nuxeo 5 exposes dozens of extension points. [Definition: An extension point a place where a component allows other components to extend its functionality.] At a high level, an extension point is a way that a component can say, "you are likely to want to put some code here later." For example, consider an imaginary component org.acme.calculator that exposes the extension point trigFunction. The calculator component is suggesting, we might even say "announcing", to the world that if some other component inside the container has a trigonometric function that might be useful to add to the calculator, the calculator component will utilize that new function.

Let us revisit our previous, fictional component and bundle, org.nuxeo.foo and imagine that the component has an implementation of a special trigonometric function called reverseTangentOnMars. The component's manifest file would need to change to inform the container about foo's contribution to the extension point. [Definition: We use the term contribution to refer to the functionality supplied to the extension point by another component.] We often use the verb "export" or "exposes" for the component that supplies the extension point. Thus, our example is one that has “the calculator component exposing the extension point trigFunction” and “the foo component contributing reverseTangentOnMars to that extension point.

The slightly changed manifest file for org.nuxeo.foo looks like this:

#
# The manifest is located in the jar file of the bundle at META-INF/MANIFEST.MF
#
Bundle-Version: 5.2.0
Bundle-Name: nuxeo's foo project 
Bundle-SymbolicName: org.nuxeo.foo 
Nuxeo-Require: org.nuxeo.bar, org.acme.calculator
Nuxeo-Component: OSGI-INF/calc-contrib.xml

You will note that our bundle now also requires the calculator bundle and multiple dependencies can be separated with a comma.

The last line of the manifest file, marked with Nuxeo-Component, refers to another file inside the bundle's jar file. That file is in the OSGI-INF directory and is called calc-contrib.xml. It is customary for bundles that want to make contributions to extension points to put the configuration files in this directory and have the configuration fie name end with -contrib.xml. Let's examine a slightly simplified, yet still imaginary, version of this file:

<?xml version="1.0"?>
<!--
 The contribution to the calc extension point is contained in the jar file
 of the bundle at OSGI-INF/calc-contrib.xml.
 This file's content is an xml fragment.
-->

<component name="org.nuxeo.foo.trig">
  <extension target="org.acme.calculator" point="trigFunction">
    <trigfunction buttonName="rtom" longName="reverseTangentOnMars"
        implementedBy="org.nuxeo.foo.ReverseTangentOnMars"/>
  </extension>
</component>

Extension point declarations like the one above are actually instructions for two different pieces of code, the OSGi container and the component that exposes the extension point. The XML nodes component and extension are intended for the container and the innermost tag, trigfunction in this case, is for the extension point itself, part of the org.acme.calculator component. The outermost tag, sadly, is called "component" and this is a bit confusing. For now, it is ok to think of this as creating a special component-like org.nuxeo.foo.trig-that exists only as the contribution to the extension point. (Strict OSGi has no notions of extension points, so Nuxeo was forced to extend things a bit.) The name of this "special purpose" component must be unique; it is normal to use a name that is prefixed by the name of the surrounding OSGi component to insure there is no collision of names, as we have done above.

The extension tag is used to tell the container which extension point is being contributed to and which component owns that extension point. If the extension point cannot be found as part of the bundle named as the target, an error will be generated and the bundle foo will likely be rejected for the normal component life cycle.

The XML fragment inside the extension tag is completely defined by the extension point. Different extension points obviously need different information about the contribution being provided, so the documentation for each extension point must be consulted to see exactly what is "needed" to contribute to the extension point. In this example, we have imagined that the calculator component is displaying a user interface so it needs both a buttonName and a longName to show the user. Further, the calculator is expecting to receive the name of a Java class contained in the contributor's bundle that meets some Java interface (defined elsewhere in the documentation!) that can actually perform the function contributed. This is supplied by org.nuxeo.foo in the implementedBy attribute; this value would likely be checked by the exposing component to insure that it met the correct interface or had other desired properties.

The theory behind extension points should be clear even from this simple example: Extension points allow code written by one party-such as the calculator implementor-to be extended by another bundle supplied at a later time that is (and was) unknown to the calculator implementors. As we will see in the later sections, the Nuxeo platform exploits this ability in a great many ways.

4.6. Bundles, components, and extension points in action

If you navigate inside your Nuxeo installation, you will be able to see the complete list of the bundles that come with the download of Nuxeo EP:

iansmith@Photo /nuxeo
$ cd /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear
iansmith@Photo /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear
$ ls -l system | wc -l
126
iansmith@Photo /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear
$ cp system/nuxeo-platform-audit-client-5.1.6.jar  /tmp
iansmith@Photo /nuxeo/tools/NuxeoEP5/NuxeoServer/server/default/deploy/nuxeo.ear
$ cd /tmp
iansmith@Photo /tmp
$ jar xf nuxeo-platform-audit-client-5.1.6.jar
iansmith@Photo /tmp
$ cd META-INF

In the listing above we have navigated to the directory of the Nuxeo server, nuxeo.ear, that contains the server's code and the accompanying bundles. We then used ls and wc to count the number of bundles shipped with the system. The total is about 125 (there are a couple of extra lines in the ls output). We have chosen a bundle and copied it to the /tmp directory which we then switched to and unpacked the bundle with the jar command. Since jar unpacks the bundle by default in the current directory, three new directories have been created, org, META-INF, and OSGI-INF. The org directory and its subdirectories contain the compiled code for this bundle, so we will ignore that for now. We have gone into the META-INF directory to examine this bundle's manifest file. This is the full MANIFEST.MF of a real bundle:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: julien
Build-Jdk: 1.5.0_16
Bundle-ManifestVersion: 1
Bundle-Version: 5.1.6.0-t20081007-151949
Bundle-Name: Nuxeo ECM Audit Web Fragment
Bundle-SymbolicName: org.nuxeo.ecm.platform.audit.web;singleton:=true
Bundle-Category: web,stateless
Require-Bundle: org.nuxeo.ecm.actions,org.nuxeo.ecm.platform.audit.api
 ,org.nuxeo.ecm.webapp.core
Provide-Package: org.nuxeo.ecm.platform.audit.web,org.nuxeo.ecm.platfo
 rm.audit.web.delegate,org.nuxeo.ecm.platform.audit.web.listener,org.n
 uxeo.ecm.platform.audit.web.listener.ejb,org.nuxeo.ecm.platform.audit
 .web.listener.events
Nuxeo-Component: OSGI-INF/actions-contrib.xml
Nuxeo-RequiredBy: org.nuxeo.ecm.war

While this bundle has some extra information that our previous, simplified examples did not, a lot of this information is informational and not critical to the correct functioning of the bundle. A key item to notice is the Bundle-SymbolicName field that we discussed before: As you would expect, it contains the bundle's name in the typical format. However, it contains a suffix, ;singleton=true. Roughly, this means that there can be only one copy of this bundle in the container at a time. For now, all the bundles you write should also have this suffix on their symbolic name field.

As you can see from this example, you can split a long line in a manifest file over two or more lines if you begin the 2nd and all following lines with a space. While this is good to know when reading manifests, normally this type of formatting is handled for you by the tool that builds your jar file, so you can simply write your manifest with long lines. You can see that this bundle provides a large number of Java packages; these are associated with the Provide-Package key. Finally, you can see the Nuxeo-Component line that references the contribution to an extension point contained in another file of the bundle. We will now look at this contribution file, actions-contrib.xml, in the directory OSGI-INF.

<?xml version="1.0" encoding="UTF-8">

<component name="org.nuxeo.ecm.platform.audit.web">
  <documentation>

      NXAudit action contributions.

      @version 1.0
      @author <a href="mailto:[email protected]">XXXXX XXXXX</a>
  </documentation>

  <extension target="org.nuxeo.ecm.platform.actions.ActionService" point="actions">

    <documentation>
      Contribute a new action related to document history.
    </documentation>

    <action id="TAB_CONTENT_HISTORY" link="/incl/tabs/document_history.xhtml"
      enabled="true" label="action.view.history" icon="/icons/file.gif"
      order="100">
      <category>VIEW_ACTION_LIST</category>
      <filter id="view_content_history">
        <rule grant="false">
          <type>WorkspaceRoot</type>
          <type>SectionRoot</type>
          <type>Server</type>
        </rule>
      </filter>
      <filter-id>mutable_document</filter-id>
    </action>

  </extension>
</component>

The purpose of this contribution is to add a new action, a new thing the user can do with a document, to documents of the type mutable-document. The details of the contribution are not particularly important at this point, it is the structure that matters. This contribution's XML fragment has the two outermost tags, component and extension, that tell the Nuxeo server (the container) which extension point is being contributed to. Each of these tags can optionally contain a documentation node and this author has graciously done so both for the component and the extension. We apologize for skimping on documentation in our last example, but it seemed unnecessary clutter as we explained extension points! The component that is exporting this extension point is the ActionService, shown in the target attribute of the extension tag in its fully-qualified form, and the extension point is called actions as shown in the point attribute.

The child of the extension node (after the useful documentation node) is the meat of this contribution. This particular snippet of XML is unique to this extension point and only by looking at the documentation for the extension can one decipher the meaning of all the various parts of this fragment. We will not bother to explain the particulars of this here, but only ask the reader to note that this is related to the user interface shown by the Nuxeo server through the web since it has references to xhtml and gif files.

4.7. The big picture

It is worth thinking more globally about what has been covered in the last three lessons. You have learned

  • The Nuxeo system is written in Java

  • The Nuxeo server is an OSGi container that manages bundles of Java code

  • The Nuxeo server is launched and contained within a complete application server (JBoss)

  • The Nuxeo server exposes a web interface (via JBoss) that allows you to accomplish ECM tasks through your web browser

  • The Nuxeo systems ships with a large number of bundles

  • The Nuxeo bundles use the same mechanisms, such as OSGi components and extension points, as any other program would

What does this mean when taken altogether?

  1. The Nuxeo server is "piggybacking" on the abilities of JBoss to allow web access. This is not a "built in" property of OSGi containers.

  2. The Nuxeo server itself is just a shell ("container") that could contain any type of application

  3. The additional bundles that "come with" the Nuxeo server provide the ECM functionality as OSGi components

  4. By choosing a set of bundles, yours or those written by Nuxeo, you can create different sets of functionality and thus different "ECM-oriented applications"

At the beginning of this book, we said that this was a book for Java programmers who would like to develop ECM-oriented applications. We hope it is now clear both how you do that - write your own bundle and load it into the Nuxeo container - and why we have introduced you to the concepts covered in these first few lessons. You are now ready to write a bundle for Nuxeo... and change the world.