Blog / 2007-02-19 aQute - Software Consultancy
Search
*

The Bundle Class Path

Last week I got a question about the Bundle-ClassPath header in OSGi. The real problem turned out to be a bug in bnd, but I thought I could clarify better some issues around the Bundle-ClassPath header.

The Bundle-ClassPath header is a way to keep your JAR structured and not flattened out. It allows you to place resources in your bundle's JAR on the search path of the framework. By default, the Bundle-ClassPath is set to the bundle itself. The Bundle-ClassPath header is one of the few headers that has a default. The bundle itself is reflected as '.', therefore the default is:

  Bundle-ClassPath: .

When the OSGi framework loads a class from a bundle, it will consult the Bundle-ClassPath. It will look in the places indicated on the Bundle-ClassPath in the given order. All the classes are loaded by the same class loaders so the content of a package can be spread out over different places, although this is normally not recommended.

You can place 3 different elements on the Bundle-ClassPath:

  1. ., the bundle itself
  2. The name of a resource
  3. The name of a directory

For example:

  Bundle-ClassPath: .,
     /WEB-INF/classes,
     bsh-core-2.0b4.jar

Using '.'on the Bundle-ClassPath is the bundle itself; the OSGi framework will look for the resource in the root of the bundle's jar.

If you use the name of a resource, then this resource must be a JAR file. This is a convenient way to store a library in your bundle or to wrapp a library. The BundleClassPath snippet shows you how this could be done, using the beanshell as an example. Using a JAR on the Bundle-ClassPath can also be used to wrap an existing JAR and turn it into a Bundle. The bundle then only contains the required OSGi headers.

You can also use a resource directory in the bundle. The OSGi Framework will then search this directory as the root. Common example is the WAR directory with the Java classes: /WEB-INF/classes.

The framework will not complain if the resource or resource directory does not exist. These are silently ignored.

Fragments make it possible to extend the Bundle-ClassPath header. The host bundle could specify a library that is delivered as a fragment. Different fragments can then be used to deliver different variations of the same library. Fragments can even extend the Bundle-ClassPath of the host. The new entries are added at the end of the host's Bundle-Classpath.

When to Use

The key question is of course when and how to use the Bundle-ClassPath?

First, the Bundle-ClassPath carries a runtime cost in both time and memory (both persistent and runtime) consumption.

  • The framework must search a number of structures instead of one. This overhead is not high, but it does exist.
  • Internal libraries must be expanded, most frameworks copy the embedded jars to the file system.

There can be one performance advantage of using an embedded JAR: an uncompressed JAR is better compressed as an entry than a JAR with compressed entries. Ok, that previous sentence requires multiple readings. The reason is that the Lempel-Ziv algorithm needs to learn what to compress. Larger files have therefore a better compression ratios than the same file cut in pieces. However, this is only a small advantage because the uncompressed version must still be loaded in memory.

Flattening the Bundle-ClassPath is not easy with the traditional jar/ant/maven/eclipse tools. For Bnd users life is easier because bnd allows the inlining of JARs making it trivial to flatten the Bundle-ClassPath. Inlining is the process where the contents of a JAR are copied completely to the bundle entry by entry.

Some people use the Bundle-ClassPath because they think it simplifies licensing. The Bundle-ClassPath allows the inclusion of complete JAR files without unpacking them. Inlining the JARs might not be compatible with their license. Maybe, as so many things it is hard to see what the difference is: in either case you mangle to bits to create a new artifact. I sincerely doubt that one could explain the difference to a jury or judge.

Another argument is that the JARs become simpler. This is absolutely true, but then again, who looks inside JAR files? The OSGi Framework has very little sensitivity for aesthetics.

A really good reason can be WAR files. These files have their classes in a specific directory: /WEB-INF/classes. Allowing the WAR files to act as a bundle as well as a J2EE application requires the inclusion of that directory on the OSGi Framework's classpath. This use case actually drove the R4 feature to support directories on the Bundle-ClassPath.

Conclusion

If you are stuck with building your bundles with the Eclipse PDE, Maven, Ant, or plain jar tool, then the Bundle-ClassPath is the only way to use foreign code into your bundle. None of these tools support inlining of external JAR files or expansion of classes from the general class path. In such a case, the Bundle-ClassPath is an invaluable tool in your tool chest.

If you use Bnd, it is usually easier to inline or expand because it has no runtime cost and it detects a number of error cases. This does not mean that bnd does not support the Bundle-ClassPath. On the contrary, it verifies the entries in detail and takes the Bundle-ClassPath into account for its analysis and manifest generation.

Peter Kriens
Copyright 2006 aQute SARL, All Rights Reserved