Launching an OSGi app on the command line

I am a total beginner on this topic, but here is what I have found so far. Please correct me, advise me etc. in the comments.

A colleague has written a program based on OSGi, which is a framework for obfuscating everything “modular” software, which I think means loading code at runtime, even if it requires a different classloader, and even if it depends on other things which also need loading at runtime. I may be wrong though.

Anyway, all I want to do is run it (on windows only, for now).

OSGi is quite closely linked to the Eclipse development environment – Eclipse uses OSGi to manage its plugins, which are an infinitely complex web of incompatible interdependencies.

My colleague showed me how to set up their Eclipse environment, and how to run the program from within Eclipse using an OSGi launch configuration. I also managed to build the JAR files of the program itself.

What I wanted to do was run it outside Eclipse, on the command line (and eventually in an Ant build).

I found it almost impossible to understand anything others have written about OSGi, but somehow I muddled my way through to being able to do it.

Here’s how I managed it.

Find out the dependencies

If you have an Eclipse launch configuration that successfully runs your program inside Eclipse, you can find out all the OSGi bundles (which are JAR files containing extra config) that it needs. Go to the Run menu and click Run Configurations. Find the relevant run configuration, which will be inside the OSGi Framework tree item on the left. Click on it, and choose the Bundles tab. The tree view will show you all the bundles needed to run this program. If you check “Only show selected” you can see only the ones you need.

If you want a text version of this list, look for a file here:

WORKSPACE/.metadata/.plugins/org.eclipse.pde.core/LAUNCHERNAME/org.eclipse.simpleconfigurator/bundles.info

where WORKSPACE is the location of your Eclipse workspace, and LAUNCHERNAME is the name of the run configuration you found in the tree view in the Run Configurations page.

The bundles.info file contains a comma-separated list of bundle name (which is a reversed-url type name, like a package name – don’t confuse it with a package name though), some version information, and the path to the actual JAR file that is this bundle.

We will also need the Equinox OSGi bundles, including the SimpleConfigurator (oh, the irony). I am using these versions – yours might be different:

org.eclipse.equinox.launcher.win32.win32.x86_1.1.2.R36x_v20101222/*
org.eclipse.equinox.app_1.3.1.R36x_v20100803.jar
org.eclipse.equinox.common_3.6.0.v20100503.jar
org.eclipse.equinox.concurrent_1.0.100.v20100503.jar
org.eclipse.equinox.ds_1.2.1.R36x_v20100803.jar
org.eclipse.equinox.event_1.2.0.v20100503.jar
org.eclipse.equinox.frameworkadmin.equinox_1.0.200.v20100505.jar
org.eclipse.equinox.frameworkadmin_2.0.0.v20100503.jar
org.eclipse.equinox.http.jetty_2.0.0.v20100503.jar
org.eclipse.equinox.http.registry_1.1.1.R36x_v20101103.jar
org.eclipse.equinox.http.servlet_1.1.0.v20100503.jar
org.eclipse.equinox.launcher_1.1.1.R36x_v20101122_1400.jar
org.eclipse.equinox.p2.artifact.repository_1.1.1.R36x_v20100901.jar
org.eclipse.equinox.p2.console_1.0.200.v20100601.jar
org.eclipse.equinox.p2.core_2.0.3.R36x_v20110111.jar
org.eclipse.equinox.p2.director.app_1.0.202.R36x_v20110608.jar
org.eclipse.equinox.p2.directorywatcher_1.0.203.R36x_v20101220.jar
org.eclipse.equinox.p2.director_2.0.4.R36x_v20120223.jar
org.eclipse.equinox.p2.engine_2.0.1.R36x_v20110201.jar
org.eclipse.equinox.p2.extensionlocation_1.2.0.v20100518.jar
org.eclipse.equinox.p2.garbagecollector_1.0.100.v20100503.jar
org.eclipse.equinox.p2.jarprocessor_1.0.200.v20100503a.jar
org.eclipse.equinox.p2.metadata.repository_1.1.1.R36x_v20110302.jar
org.eclipse.equinox.p2.metadata_2.0.1.R36x_v20101202.jar
org.eclipse.equinox.p2.operations_2.0.0.v20100510.jar
org.eclipse.equinox.p2.publisher_1.1.2.v20100824-2220.jar
org.eclipse.equinox.p2.ql_2.0.0.v20100503a.jar
org.eclipse.equinox.p2.reconciler.dropins_1.1.3.R36x_v20111213.jar
org.eclipse.equinox.p2.repository.tools_2.0.1.R36x_v20100823.jar
org.eclipse.equinox.p2.repository_2.0.2.R36x_v20110111-1500.jar
org.eclipse.equinox.p2.touchpoint.eclipse_2.0.4.R36x_v20110908.jar
org.eclipse.equinox.p2.touchpoint.natives_1.0.201.R36x_v20110111.jar
org.eclipse.equinox.p2.ui.sdk.scheduler_1.0.0.v20100507-1815.jar
org.eclipse.equinox.p2.ui.sdk_1.0.100.v20100513.jar
org.eclipse.equinox.p2.ui_2.0.0.v20100518.jar
org.eclipse.equinox.p2.updatechecker_1.1.101.R36x_v20100823.jar
org.eclipse.equinox.p2.updatesite_1.0.201.R36x_v20100823.jar
org.eclipse.equinox.preferences_3.3.1.R36x_v20110302.jar
org.eclipse.equinox.registry_3.5.0.v20100503.jar
org.eclipse.equinox.security.ui_1.0.200.v20100503.jar
org.eclipse.equinox.security.win32.x86_1.0.200.v20100503.jar
org.eclipse.equinox.security_1.0.200.v20100503.jar
org.eclipse.equinox.simpleconfigurator.manipulator_2.0.0.v20100503.jar
org.eclipse.equinox.simpleconfigurator_1.0.200.v20100503.jar
org.eclipse.equinox.util_1.0.200.v20100503.jar
org.eclipse.osgi.services_3.2.100.v20100503.jar
org.eclipse.osgi.util_3.2.100.v20100503.jar
org.eclipse.osgi_3.6.50.R36x_v20120315-1500.jar

(I have no idea whether you need all of these – sorry.)

Make a fake environment

Imagine you have a program that consists of 2 JARs itself, called my_program_main_1.0.jar and my_program_lib_1.1.jar, 2 dependency bundles called com.dep1_3.6v20091112.jar and org.dep2_1.0.jar and a directory containing a dependency bundle called org.foo.dep3_1.4.

Create a directory that will contain your program and its dependencies. Structure it like this:

main_dir/
    my_program/
        my_program_main_1.0.jar
        my_program_lib_1.1.jar
    osgi_install_area/
        plugins/
            com.dep1_3.6v20091112.jar
            org.dep2_1.0.jar
            org.foo.dep3_1.4/
                ... more stuff in here ...
            org.eclipse.equinox.launcher.win32.win32.x86_1.1.2.R36x_v20101222/
                ... more stuff in here ...
            org.eclipse.equinox.app_1.3.1.R36x_v20100803.jar
            ... lots more here ...
    configuration/
        config.ini
    bundles.info

Copy your program’s JARs into the my_program directory (feel free to give it a different name!).

Copy the Equinox bundles and the other dependencies into the plugins directory. You can find them in the directory listed in the bundles.info file you found in the previous section. The Equinox bundles should be alongside the others (hopefully).

The contents of bundles.info should be something like this:

#version=1
MyProgramMain,1.0,file:../my_program/my_program_main_1.0.jar,4,true
MyProgramLib,1.1,file:../my_program/my_program_lib_1.1.jar,4,true
com.dep1,3.6v20091112,file:./plugins/com.dep1_3.6v20091112.jar,4,true
org.dep2,1.0,file:./plugins/org.dep2_1.0.jar,4,true
org.foo.dep3,1.4,file:./plugins/org.foo.dep3_1.4/,4,true
org.eclipse.equinox.launcher.win32.win32.x86,1.1.2.R36x_v20101222,file:./plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.1.2.R36x_v20101222/,4,true
org.eclipse.equinox.app,1.3.1.R36x_v20100803,org.eclipse.equinox.app_1.3.1.R36x_v20100803.jar,4,true
... Plus similar lines for all the equinox and OSGi bundles you need ...

Make sure that the version number after the first comma exactly matches the version number encoded in the JAR filename.

The contents of config.ini should be something like this:

osgi.bundles=reference\:file\:C\:/absolute/path/to/main_dir/osgi_install_area/plugins/org.eclipse.equinox.simpleconfigurator_1.0.200.v20100503.jar@1\:start
org.eclipse.update.reconcile=false
osgi.bundles.defaultStartLevel=4
osgi.install.area=file\:C\:/absolute/path/to/main_dir/osgi_install_area
osgi.framework=file\:C\:/absolute/path/to/main_dir/osgi_install_area/plugins/org.eclipse.osgi_3.6.50.R36x_v20120315-1500.jar
org.eclipse.equinox.simpleconfigurator.configUrl=file\:/C\:/absolute/path/to/main_dir/bundles.info
osgi.configuration.cascaded=false

Note that these paths have to be absolute. I plan to write a small ant script to create the config.ini file when needed.

Run the program

Now, to run your program, it’s a simple matter of getting into the right directory:

cd "C:\absolute\path\to\main_dir"

and typing this simple command:

java -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=Cp1252 -classpath "osgi_install_area\plugins\org.eclipse.equinox.launcher_1.1.1.R36x_v20101122_1400.jar" org.eclipse.equinox.launcher.Main -configuration "file:configuration/" -os win32 -ws win32 -arch x86 -nl en_GB -console ACTUAL_ARGS_TO_PROGRAM

Where ACTUAL_ARGS_TO_PROGRAM is where you type the actual command-line arguments (if any) you want to pass to your program.

Disclaimer

I have no idea what most of this means, and I’ve probably told you a lot of things that are wrong. Please, people who know about this stuff, correct me and I’ll update this post.

This worked for me, but I feel like I must be doing something wrong – can it really be this difficult?