Hello OSGi

Mini example - instantiating the framework to call a "main" method

We all know OSGi is great! It is simple to learn and use. However, some might find hard starting an application, even some that might be heavy users of this framework. In this post, we will see why that is a weird reality and a simple example to help first time users.

Firstly, this happens because we normally see work being done in applications that are already in a not so early stage of development or using frameworks and environments that embedded OSGi inside themselves, wrappers are a common place rather than the exception. The framework is so easy and simple, that some might not even be aware they are using it.

For instance, imagine a Liferay project, where you get the Liferay workspace set up in the IDE of your choosing (if you do not already have a bundled IDE), and start coding. Your code is "absorbed" by Liferay as a plugin, hot deployable or not, and it is stated for you. If not running yet, one just needs to start Tomcat (as an example of possible Servlet containers), which will start Liferay that will initiate the OSGi container and finally your plugin. In this scenario, you have not even touched OSGi to make it start, and that is just normal.

One can develop for years for an OSGi based product without even knowing OSGi is there or knowing only how it works after it is started, they might use the service layer and be familiar with the bundle lifecycle without never starting the framework itself. As if this was not enough, the documentation is normally heavy in technical details and some might feel that they need to know OSGi to read the documentation.

At this point I would like to show an example, but first a warning for those that already work with OSGi and are just trying to learn how to start it like those I mentioned before: we are not yet in a OSGi environment even after starting it. The start up code that will be shown is plain Java.

There are ways to make OSGi provide services and access to components to the non-OSGi areas of the application, but that is not what we will do here. Well, it is, but the goal here is to bootstrap the framework to run from a given entrypoint.


The app

Ok, let's start:


import java.lang.reflect.InvocationTargetException;

public class Application {

	public static void main( String[] args ) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

		FelixRuntime runtime = new FelixRuntime( );
		try {
			runtime.startConsoleService( );
		}
		finally {
			runtime.stop( );
		}
	}
}

The idea is simple, we first have to prepare the runtime environment, and in this case we will create a instance of Framework, the Felix implementation. After, we will ask the runtime to start our app.

* We will talk about dependencies later, but for the ones in a hurry: downloading-the-framework

In this tiny example we are calling a constructor for our framework, and asking that object to start the console service, which is the real application we want to start. The example is different from regular deployments where the framework just runs as a set of bundles that create a collective behaviour when responding to events. The difference here is that we are using the framework to call a specific piece of functionally, an entry point as a main is to a Java program.


Starting the framework

Now let's see the class we coded to represent the runtime environment:


package org.example.tasks.demo.app;

import org.apache.felix.main.AutoProcessor;
import org.osgi.framework.*;
import org.osgi.framework.launch.*;

import java.lang.reflect.*;
import java.util.*;

class FelixRuntime {

	private static Framework felix;
	private static String CONSOLE_SERVICE = "org.example.demo.app.console.Console";

	FelixRuntime( ) {

		loadFelix( );
	}

	public void stop( ) {

		try {
			if ( felix != null ) {
				felix.stop( );
				felix.waitForStop( 0 );
			}
		}
		catch ( Exception ex ) {
			System.err.println( "Error stopping framework: " + ex.getMessage( ) );
			ex.printStackTrace( );
			System.exit( -1 );
		}
	}

	private void addShutdownHook( ) {

		Runtime.getRuntime( )
			   .addShutdownHook( new Thread( "Felix Shutdown Hook" ) {

				   public void run( ) {

					   try {
						   if ( felix != null ) {
							   felix.stop( );
							   felix.waitForStop( 0 );
						   }
					   }
					   catch ( Exception ex ) {
						   System.err.println( "Error stopping framework: " + ex );
					   }
				   }
			   } );
	}

	private void loadFelix( ) {

		Map< String, String > configMap = new HashMap<>( );
		configMap.put( AutoProcessor.AUTO_DEPLOY_DIR_PROPERTY, "bundle" );
		configMap.put( AutoProcessor.AUTO_DEPLOY_ACTION_PROPERTY, "install,start" );
		configMap.put( Constants.FRAMEWORK_STORAGE_CLEAN, "onFirstInit" );
		configMap.put( "felix.fileinstall.dir", "hot-plugins" );
		configMap.put( "felix.log.level", "2" );

		try {
			FrameworkFactory factory = getFrameworkFactory( );
			felix = factory.newFramework( configMap );
			felix.init( );
			AutoProcessor.process( configMap, felix.getBundleContext( ) );
			felix.start( );
			addShutdownHook( );
		}
		catch ( Exception ex ) {
			System.err.println( "Could not create framework: " + ex.getMessage( ) );
			ex.printStackTrace( );
			System.exit( -1 );
		}
	}

	private FrameworkFactory getFrameworkFactory( ) {

		ServiceLoader< FrameworkFactory > factoryServiceLoader = ServiceLoader.load( FrameworkFactory.class );
		return factoryServiceLoader.iterator( )
		                           .next( );
	}

}

This is utterly simple. Let's start by the load method. We configure, then we ask a factory to create an instance with that configuration. We are mixing the usage of constants and hard coded values to also show that the constants come from varied places, and some are hard to find. The fact that we are using only one map to hold the configuration for the auto processor, for the framework and for specific Felix properties leads to this "Frankenstein". But let's keep short for this post.

To define Felix as the the framework implementation to be used, we ask for a factory using the standard Java way of mapping a service to an implementation class: using a ServiceLoader


    private FrameworkFactory getFrameworkFactory( ) {

		ServiceLoader< FrameworkFactory > factoryServiceLoader = ServiceLoader.load( FrameworkFactory.class );
		return factoryServiceLoader.iterator( )
		                           .next( );
    }

This method will require a mapping file, named after the service it is supposed to map. In the case of this example, we have a file named after FrameworkFactory.class in the META-INF folder:

File: ./META-INF/services/org.osgi.framework.launch.FrameworkFactory

org.apache.felix.framework.FrameworkFactory

We need a factory that implements the standard interface as defined by the OSGi specification. In our case, we are looking for the implementation that provides Felix. So we configured our choice by declaring the factory that ships with Felix, in their main JAR file as: org.apache.felix.framework.FrameworkFactory.


Stopping the framework

Ok, we know how to start the framework, let's see now how to stop it.

It is utterly simple: if not null, stop it.


	public void stop( ) {

		try {
			if ( felix != null ) {
				felix.stop( );
				felix.waitForStop( 0 );
			}
		}
		catch ( Exception ex ) {
			System.err.println( "Error stopping framework: " + ex.getMessage( ) );
			ex.printStackTrace( );
			System.exit( -1 );
		}
	}

Quite simple, but there is a catch, the stop method is asynchronous. For this reason, we also asked for the thread to wait for the stopping sequence to be completed. Fortunately, the hard part of this flow is given to us. We could also not call that method directly but put it in a JVM hook when the instance is created. To address concurrency issues, this method could be synchronized and the reference nulled out. But let's keep simple for this post and image that in this application we have only one possible process able to call the stop function (in the application class or the less reliable hook).


	private void addShutdownHook( ) {

		Runtime.getRuntime( )
			   .addShutdownHook( new Thread( "Felix Shutdown Hook" ) {

				   public void run( ) {

					   try {
						   if ( felix != null ) {
							   felix.stop( );
							   felix.waitForStop( 0 );
						   }
					   }
					   catch ( Exception ex ) {
						   System.err.println( "Error stopping framework: " + ex );
					   }
				   }
			   } );
	}


Running the target

You are probably wondering where is the code that actually runs the target method, the one that we actually want to execute. There is a reason you have not found it, I have hidden it here to discuss the necessary piping first. Let's see it, and yes, it is with reflection.


	void startConsoleService( ) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

		BundleContext context = felix.getBundleContext( );

		ServiceReference serviceReference;

		do {
			serviceReference = context.getServiceReference( CONSOLE_SERVICE );
		} while ( serviceReference == null );

		Object service = context.getService( serviceReference );

		Method run = service.getClass( )
				    .getMethod( "run" );

		run.invoke( service );
	}

First, we need to get a bundle context by using the felix instance. Remember, we cannot just request the framework for a component as we are not inside OSGi here but we can ask the instance for its context as the initial system bundle is still a bundle, besides being a Framework as Framework extends Bundle. In this example we used a very rudimentary waiting loop, because it might take a moment for the framework to load the bundle we want, perhaps from a hot deployment, but the fact is that it is not a simple request for a reference like in @Reference.

We use the service reference to get the component, but instead of using a more specific class as type, we use Object. This was written like that to deal with simple classloader conflicts.

Let's say the class we need is the the interface that represents the service we are looking for. We add the Jar file that contains that class to the application classpath, as usual. But when we request OSGi for an instance of that class, as a component implementing that service, we get a loud exception that is just too weird for Java developers not used to work in environments with multiple classloaders.

It is not just the name of the class that matters to say that a class is, well, the class. A class is also defined by its classloader, and as such, the class loaded when we started our app is not the same OSGi uses, due to the single fact that every bundle gets its own classloader. If we were inside OSGi, that would be a different case, because only one bundle will resolve the class and be selected to provide it for you (if you are not on those cases similar to when different bundles using different versions of a class exchange objects through a path leading to both being used in a common point, resulting in cast issues...).

Back on track; after getting the object that provides the service, we select the method and arguments we need to call, invoking it from the same object we used to get the class from where we got the method.


That is all for this short post. It may seem like there is a lot of piping, but they are trivial connections and a short number of lines in a real application, especially if inside an enterprise app. Also, we are running a not so usual case, instantiating the framework to call a "main" method. The OSGi distribution comes with a default configuration in a file, so we do need to adjust only what we need. It also comes with the META-INF and a main class to start the framework and leave it running, mostly responding to events in a collective defined behaviour. Nevertheless, this example is cool,

More Blog Entries

0 Comments