Donnerstag, 17. Januar 2013

Embedding OSGi

The guitool in OSGi snippets is a plain Java application. In order to use OSGi, the OSGi framework must be started somehow. This is usually called "embedding the OSGi framework". In the OSGi snippets project the class OSGiFwLoader takes care of that.

Using OSGiFwLoader

The class OSGiFwLoader is used like this:

OSGiFwLoader osgiFwLoader = new OSGiFwLoader(); osgiFwLoader.start();

Eventually the framework needs to be stopped:

osgiFwLoader.requestStop();osgiFwLoader.waitForStop();

Usually bundles should be installed:

Bundle bundle = osgiFwLoader.installBundle( new File("somebundle.jar"));

and started:

osgiFwLoader.start(bundle);

Implementation of OSGiFwLoader

The constructor does nothing, so the first interesting method is start():

public void start() { logger.info("Starting framework..."); try { Map<Object,Object> configMap = new HashMap<Object, Object>(); prepareConfigMap(configMap); FrameworkFactory fwf = new FrameworkFactory(); fw = fwf.newFramework(configMap); fw.init(); fw.start(); } catch (Exception e) { e.printStackTrace(); } }

The relevant lines are in black. After the fw.start() call, the OSGi framework is up and running. The configMap contains configuration data, I will come back to that later.

In the guitool there is a button labelled "Start Framework". When that button is pressed, among a few other things, OSGIFwLoader.start() is called. After the framework has been started, the list of bundles has one entry:

As the text reads, it is the "System Bundle" which is the framework itself. An overview can be found here http://wiki.osgi.org/wiki/System_Bundle. The bundle id is 0 (zero) and the bundle symbolic name is "org.apache.felix,framework". The symbolic name of the system bundle is implementation dependent. Apache Felix is used here, hence the name.

Things are rather boring without other bundles, so the OSGi loader should be able to install bundles:

public Bundle installBundle(File file) { Bundle bundle; try { BundleContext bc = fw.getBundleContext(); String url = new URL("file", "", file.getPath()).toString(); bundle = bc.installBundle(url); } catch (BundleException | MalformedURLException e) { logger.error("Error installing bundle", e); bundle = null; } return bundle; }

The relevant method call is BundleContext.installBundle() here. It takes the location of the bundle file as a parameter and returns a reference to the bundle instance.

If needed, the returned bundle reference is used to start the bundle later. So its time to look at this method:

public void startBundle(Bundle bundle) { logger.info("Starting bundle..."); if (isFragmentBundle(bundle)) { logger.warn( "Bundle is a Fragment-Bundle, won't start bundle {}", bundle); return; } try { bundle.start(); } catch (BundleException e) { logger.error("Error starting bundle", e); } catch (Exception e) { logger.error("Error starting bundle", e); } }

More methods exist to stop and uninstall a bundle.

Eventually the framework should be stopped somehow. The first step is to tell the framework, that it should stop and the second step is to wait for the framework to have stopped.

public void requestStop() { logger.info("Stopping framework..."); try { fw.stop(0); } catch (BundleException e) { logger.error("Error during framework stop, ignored.", e); } }

The stop method returns immediately and the framework does the actual work in other threads. To wait for the framework to have stopped, the loader class offers the following method:

public void waitForStop() { logger.info("Waiting for framework to stop..."); try { fw.waitForStop(0); logger.info("Framework stopped..."); } catch (Exception e) { logger.error("Error while waiting", e); } }

The two methods are basically wrappers around the Framework's methods to catch Exceptions. Since the Exceptions are only logged and the caller of the OSGiFwLoader's methods wouldn't know, if any were thrown, the implementation is by far not production ready. So if you use the code, you need to change the error handling.

Going back to starting the framework, there was configuration data involved. OWGiFwLoader.start() calls this method:

private void prepareConfigMap(Map<Object, Object> configMap) { // List the packages which shall be exported by the System Bundle. configMap.put( Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "org.slf4j"); // Each time the OSGi Framework starts, it shall clean up its // storage. I decided to do that, so one gets a clean environment // each time. configMap.put( Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); }

When embedding an OSGi framework into an application, that application usually provides services to bundles or uses services provided by bundles. The packages of these services must be accessible and this is accomplished with the entry Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA. In the OSGi snippets project, the slf4j logging library is used and is in the classpath of the application. The package org.slf4j is added to the configuration entry, so it is available to bundles.

The entry FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT makes sure, that the storage maintained by the OSGi implementation is cleaned each time it is run. Usually this is not needed, but for something which is used for playing around, I prefer to clean up frequently.

Resources

Keine Kommentare:

Kommentar posten