1. Concepts

SpringSource dm Server is a Java application server composed from a collection of modules which supports applications which are also composed from a collection of modules. These may be traditional Java web applications packaged as Web ARchive (.war) files as well as other modular applications.

This chapter introduces concepts necessary for developing dm Server applications. These concepts will become clearer as the GreenPages application is explored in later chapters.

1.1 OSGi concepts

Modules in dm Server are represented using a standard Java module system known as OSGi. Modules consist of programs and resources organised by Java package together with metadata which declares imported and exported packages. A module exports a package to make the corresponding programs and resources available for use by other modules. A module imports a package to use the corresponding programs and resources of another module.

Representing a program as a collection of modules makes it easier for the programmer to manage it and modify it and for teams of programmers to divide responsibilities between themselves. A module is similar to a Java class in this respect. Rules similar to those for organising data and programs into classes can be applied to organising applications into modules.

An industry consortium known as the OSGi Alliance (see the appendix Projects) develops the OSGi specification, reference implementation, and compliance tests. dm Server is built on the Equinox OSGi framework which is also the reference implementation for OSGi.

Bundles

Modules in OSGi are known as bundles. Each bundle conforms to the JAR file format and can contain Java classes, a manifest (in META-INF/MANIFEST.MF), and further resource files.

The OSGi framework enables bundles to be installed and run.

Bundles are identified by OSGi in two distinct ways: by symbolic name and by bundle id.

A bundle usually declares its symbolic name in its manifest, like this:

Bundle-SymbolicName: org.foo.bundle

Additionally, the OSGi framework assigns a number, known as a bundle id, to each bundle as it is installed. The OSGi framework itself resides in a bundle with bundle id 0.

The dependencies between bundles are expressed statically in terms of packages and dynamically in terms of services. A package is familiar to Java programmers. For example, a Java program may depend on a class org.foo.X and a bundle containing that program would either need to contain org.foo.X or depend on the package org.foo. Package dependencies are specified in the bundle manifest, for example:

Import-Package: org.foo

A bundle which provides a package for use by other bundles must export the package in its manifest. For example:

Export-Package: org.foo

The OSGi framework ensures that a given bundle’s package dependencies can be satisfied before the bundle runs. This process is known as resolution.

After a bundle is resolved, its classes and resources are available for loading. In OSGi, bundles and their packages do not appear on the application classpath. Instead, each bundle has a class loader which loads its own classes and loads classes belonging to each of its imported packages by deferring to the bundle class loader that exported the package.

Life cycle

The OSGi framework manages the life cycle of each bundle. A bundle is first of all installed and will be in the INSTALLED state. If a request is made to start the bundle, the OSGi framework resolves the bundle and, if resolution was successful, will subsequently move the bundle to the ACTIVE state. If a request is made to stop the bundle, the OSGi framework will move the bundle back to the INSTALLED state. A request may then be made to uninstall the bundle.

While the bundle is INSTALLED or ACTIVE, it may be updated to pick up some changes. These changes are not detected by bundles which were depending on the bundle before it was updated. A “refresh packages” operation may be performed to ripple the changes out to those bundles. (See Services concepts.)

The life cycle of a bundle is best summarised as a state transition diagram. This diagram shows some more of the intermediate states of a bundle not described in the overview above:

Figure 1.1. Bundle States

Bundle States


Services

Bundles may publish Java objects, known as services, to a registry managed by the OSGi framework. Other bundles running in the same OSGi framework can then find and use those services. Services are typically instances of some shared Java interface. A bundle which provides a service need not export the package containing the implementation class of the service.

For example, a bundle could export a package containing the interface org.bar.SomeInterface, thus:

Export-Package: org.bar

implement the interface with a class SomeImpl:

package org.bar.impl;

class SomeImpl implements SomeInterface {
	…
}

create an instance of SomeImpland then publish this instance (as an instance of the interface SomeInterface).

OSGi specifies a number of standard services. For example, the Package Admin service provides the “refresh packages” life cycle operation mentioned above.

OSGi provides an API which can be used to publish and find services, but it is much simpler to use Spring DM to accomplish this. (See Spring DM concepts.)

Versioning

OSGi supports versioning of bundles, packages, and several other entities, and provides some mechanisms for managing these versions.

Version numbers

An OSGi version number consists of up to three numeric components or exactly three numeric components followed by a string component. The components are separated by a period (“.”) and are called the major, minor, micro, and qualifier components, respectively. For example, the version 2.4.1.ga has major component 2, minor component 4, micro component 1, and a qualifier component ga. (There are restrictions on the characters that can appear in a qualifier. For example, letters, digits, underscores and hyphens are allowed; periods and commas are not.)

Trailing components may be omitted along with their . So, for example, the version numbers 2, 2.0, and 2.0.0 are all equal. This example demonstrates that 0 is assumed if a numeric component is omitted, and the empty string is assumed for an omitted qualifier.

Version ranges

Dependencies on bundles and packages have an associated version range which is specified using an interval notation: a square bracket “[” or “]” denotes an inclusive end of the range and a round bracket “(” or “)” denotes an exclusive end of the range. Where one end of the range is to be included and the other excluded, it is permitted to pair a round bracket with a square bracket. The examples below make this clear.

If a single version number is used where a version range is required this does not indicate a single version, but the range starting from that version and including all higher versions.

There are three common cases:

  • A “strict” version range, such as [1.2,1.2], which denotes one version and only that version.

  • A “half-open” range, such as [1.2,2), which has an inclusive lower limit and an exclusive upper limit, denoting all versions from version 1.2 up to version 2.0.0 but not including version 2.0.0.

  • An “unbounded” version range, such as 1.2, which contains version 1.2 and all higher versions.

Versioning policies

A versioning policy is a way of using version numbers to indicate compatible and incompatible changes. OSGi does not mandate a particular versioning policy. Instead a specific versioning policy may be implemented using version ranges.

Strict and half-open version ranges are most useful in representing versioning policies. Unbounded version ranges can lead to problems as they assume that compatibility will be preserved indefinitely.

For example, a conservative versioning policy may assume that any change other than in the qualifier component of a version implies an incompatible change to the object. Such a policy would employ version ranges such as [1.2.1.beta,1.2.2) which accept any version from 1.2.1.beta inclusive to 1.2.2 exclusive. For another example, a relaxed versioning policy may assume that only changes in the major component of a version denote an incompatible change. Such a policy would employ version ranges such as [1.2,2).

Bundle version

Each bundle has a version. The bundle’s version may be specified in the manifest using a Bundle-Version header:

Bundle-Version: 1.4.3.BUILD-20090302

If not specified the bundle version is assumed to be 0.

Package version

Each exported package has a version. The exported package’s version may be specified on the Export-Package manifest header. For example

Export-Package: org.foo;version="2.9",org.bar;version="1"

exports two packages: org.foo, at version 2.9.0 and org.bar, at version 1.0.0.

If the version attribute is omitted the version is assumed to be 0.

Each package import has a version range. The package import version range may be specified on the Import-Package manifest header. If interval notation is used, the version range must be enclosed in double quotes, for example:

Import-Package: org.foo;version="[2,3)",org.bar;version="[1,1]"

seeks to import a package org.foo in the range [2.0.0,3.0.0) and a package org.bar with the (exact) version 1.0.0.

If a version range is not specified on an import, the range 0 is assumed, meaning that any version of this package would satisfy the import.

Bundle manifest version

Bundle manifests have a version which is 1 by default, indicating OSGi Release 3 semantics. dm Server is based on OSGi Release 4 and therefore expects bundle manifests to be at version 2, indicating OSGi Release 4 semantics. (See the appendix Projects.) The bundle manifest’s version should be specified on the Bundle-ManifestVersion manifest header, exactly as follows:

Bundle-ManifestVersion: 2

Manifest version

Manifests themselves also have a version which must be specified as 1.0. This is not an OSGi definition but part of the JAR file specification (http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html).

Manifest-Version: 1.0