OSGi is a framework focusing on modularity of Java applications. It becomes more and more popular, so you may want to run your JGUIraffe application in an OSGi container. This is possible, but there are some aspects to be aware of. In this chapter these topics are discussed.
In order to deploy an application into an OSGi framework, it has to be packaged in so-called bundles - jar archives containing special meta data. From version 1.2 on, the JGUIraffe jar is a valid OSGi bundle. This is also true for the integration jars for the supported target platforms. They all contain the necessary meta data and thus can be directly deployed into your OSGi container of choice - you have to deploy the main jar plus the correct integration jar for the selected target platform.
The JGUIraffe modules have some dependencies to other libraries (details can be found on the Dependencies page). For a successful deployment into an OSGi container, all these dependencies have to be available as well. Unfortunately, not all of them support OSGi out of the box.
Newer releases of the Apache Commons components referenced by JGUIraffe already contain OSGi meta data, but some of the older releases do not. Especially the Commons Jelly jar is (at the time of this writing) not provided as an OSGi bundle. Because these dependencies have to be satisfied when installing the JGUIraffe bundle in an OSGi framework you have to obtain OSGi-enabled versions of the affected libraries. One option could be to re-package the jars as OSGi bundles yourself. Using the Apache felix maven bundle plug-in this is not too complicated. For instance, The Maven Cookbook contains a chapter about OSGi development which also covers the creation of OSGi bundles for existing jars.
A major feature of OSGi is that it enforces a very strict class loading architecture allowing each bundle only access to classes it explicitly references in its OSGi meta data. Especially for the dependency injection framework of JGUIraffe this has a big impact because classes have to be used which are defined in client bundles unknown to JGUIraffe.
The dependency injection framework uses an object implementing the
ClassLoaderProvider
interface to resolve classes. When starting
up, the central
Application
object creates and initializes such a
ClassLoaderProvider
object. Per default, it sets the class
loader which loaded the custom application class as default class loader.
For many simple applications this is exactly what you want because this is
typically the class loader of the client bundle. So referenced classes can
directly be obtained from this bundle. If you have a more complicated
setup, you should override the initClassLoaderProvider()
method of your Application
class to register all involved
class loaders with specific names. In your builder scripts you have to
make sure that the correct class loaders are referenced.
There is one caveat: To ensure that the current
ClassLoaderProvider
is actually used in a builder script,
always specify class references using tag attributes ending on
ClassName
. Do not use the simpler attributes with the
Class
suffix. For instance, the following fragment shows a
wrong example of declaring a FileSystemManager
bean from the Apache Commons VFS project (using a static factory method) in
an OSGi environment:
<di:bean name="fileSystemManager" beanClass="org.apache.commons.vfs2.FileSystemManager"> <di:factory> <di:methodInvocation method="getManager" targetClass="org.apache.commons.vfs2.VFS"> </di:methodInvocation> </di:factory> </di:bean>
Note the usage of the beanClass
and targetClass
attributes. The correct bean declaration is as follows:
<di:bean name="fileSystemManager" beanClassName="org.apache.commons.vfs2.FileSystemManager"> <di:factory> <di:methodInvocation method="getManager" targetClassName="org.apache.commons.vfs2.VFS"> </di:methodInvocation> </di:factory> </di:bean>
Here the problematic attributes have been replaced by variants ending on
ClassName
. There are some more attributes falling into this
category, e.g. parameterClassName
. The reason why the
attributes ending on Class
cause problems in an OSGi container
is that they are directly evaluated by the Jelly engine processing the
builder script. This engine does not know about the
ClassLoaderProvider
object and thus will probably use a wrong
class loader. The ClassName
attributes in contrast are
processed by JGUIraffe; here different class loaders can be taken
into account. Note that for each ClassName
attribute there is
a companion attribute ending on Loader
. With this attribute a
special class loader can be selected. In simple applications these
attributes are typically not needed; if they are missing the default class
loader is used. However, if an application deals with multiple OSGi
bundles (and therefore multiple class loaders), it may be necessary to
specify the correct class loader explicitly. In such a case the value of
the Loader
attribute must match the name of a class loader as
it has been registered at the ClassLoaderProvider
.
The OSGi environment also affects starting and shutdown of applications.
Rather than providing a static main()
method, application
bundles are installed in an OSGi container and are started automatically by
an OSGi-specific mechanism. One possibility is a bundle activator,
i.e. a class implementing the org.osgi.framework.BundleActivator
interface. The activator must be referenced from the bundle's manifest file.
Stand-alone JGUIraffe applications typically define a class
derived from
Application
with a static main()
method. The
method creates an instance of this class and invokes the static
startup()
method inherited from Application
. The
bundle activator has to do the same in its start()
method.
Because starting up the application is an expensive operation - bean
declarations have to be read, the UI is constructed, etc. - this should be
done in a new thread, so that control can be passed back to the OSGi
framework immediately. Also, an alternative
exit handler should be set to
prevent that the Java virtual machine is just terminated. Rather, the
OSGi framework should be shut down in a graceful way. This can be achieved
by obtaining a reference to the system bundle using the reserved bundle ID
0 and calling the stop()
method on it.
The following listing shows a full example of a bundle activator
implementation. It can serve as a basis for own implementations. It assumes
that there is a class called Main
derived from
Application
; this is the application class to be started.
public class Activator implements BundleActivator { /** The logger. */ private final Log log = LogFactory.getLog(getClass()); @Override public void start(BundleContext context) throws Exception { log.info("Starting application bundle."); final Runnable exitHandler = createExitHandler(context); new Thread() { @Override public void run() { Main main = new Main(); main.setExitHandler(exitHandler); try { Main.startup(main, new String[0]); } catch (ApplicationException e) { log.error("Could not start application!", e); } } }.start(); } @Override public void stop(BundleContext context) throws Exception { log.info("Stopping application bundle."); } /** * Creates the exit handler for the application. This handler will shutdown * the OSGi framework by stopping the system bundle. * * @param context the bundle context * @return the exit handler */ private Runnable createExitHandler(final BundleContext context) { return new Runnable() { @Override public void run() { Bundle sysBundle = context.getBundle(0); try { sysBundle.stop(); } catch (BundleException bex) { log.error("Could not stop OSGi framework!", bex); } } }; }