Launching an OSGi app on the command line
November 27, 2012 [Java, Tech]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?