Table of Contents
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).
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.
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.
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.
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.
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.
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.
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:xx@nuxeo.com">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.
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?
The Nuxeo server is "piggybacking" on the abilities of JBoss to allow web access. This is not a "built in" property of OSGi containers.
The Nuxeo server itself is just a shell ("container") that could contain any type of application
The additional bundles that "come with" the Nuxeo server provide the ECM functionality as OSGi components
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.