Jigsaw launcher for Felix
This post will show a simple launcher module for Felix. This launcher takes advantage of the exported packages list that can be obtained from Jigsaw’s modules to fill in the list of JRE’s packages, thus eliminating the need to hard code that list in Felix defaults.
Setting up the environement:
Download and extract Maven 3(.0.5) in ~/dev
Export the variables:
export M2_HOME=
/dev/apache-maven-3.0.5//dev/apache-maven-3.0.5/
export MAVEN_HOME=
export JAVA_HOME=~/dev/jigsaw/build/linux-x86_64-normal-server-release/images/jdk-module-image
export PATH=$JAVA_HOME/bin:$M2_HOME/bin:$PATH
Create the launcher
Create the directories
mkdir -p ~/dev/felix/felix-launcher/src/main/java/lh.jigsaw.felix.launcher/lh/jigsaw/felix/launcher mkdir ~/dev/felix/bundles mkdir ~/dev/felix/nonbundles mkdir ~/dev/felix/modules
Create the modules library
jmod create -L ~/dev/felix/modules
Download Felix
wget -O ~/dev/felix/nonbundles/org.apache.felix.framework-4.2.1.jar http://mirror.tcpdiag.net/apache//felix/org.apache.felix.framework-4.2.1.jar
(although Felix framework is a bundle it is used as a non bundle)
Add the pom
gedit ~/dev/felix/felix-launcher/pom.xml &
<project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”>
<modelVersion>4.0.0</modelVersion>
<groupId>lh.jigsaw</groupId>
<artifactId>jigsaw-felix-launcher</artifactId>
<version>0.1-SNAPSHOT</version>
<name>Jigsaw Felix launcher</name>
<packaging>jmod</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
</plugin>
<plugin>
<groupId>lh.jigsaw</groupId>
<artifactId>jigsaw-maven-plugin</artifactId>
<version>0.1-SNAPSHOT</version>
<extensions>true</extensions>
<configuration>
<libraryDirectory>/home/ludovic/dev/felix/modules</libraryDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>change the libraryDirectory to your appropriate path
Add the module-info
gedit ~/dev/felix/felix-launcher/src/main/java/lh.jigsaw.felix.launcher/module-info.java &
module lh.jigsaw.felix.launcher @ 0.1
{
requires jdk.jre;
class lh.jigsaw.felix.launcher.Main;
}(depends on jdk.jre to expose the JRE)
Add the Main class
gedit ~/dev/felix/felix-launcher/src/main/java/lh.jigsaw.felix.launcher/lh/jigsaw/felix/launcher/Main.java &
package lh.jigsaw.felix.launcher;
import java.io.File;
import java.io.IOException;
import java.lang.module.ModuleId;
import java.lang.module.ModuleInfo;
import java.lang.module.ModuleView;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import org.openjdk.jigsaw.SimpleLibrary;/**
*
* @author ludovic
*/
public class Main
{
public static void main(final String[] args)
{
try
{
// Extract information from Jigsaw //
final String libraryPath = System.getProperty(“sun.java.launcher.module.library”);
final SimpleLibrary library = SimpleLibrary.open(new File(libraryPath));
final StringBuilder packages = new StringBuilder();
final List<URL> urls = new ArrayList<>(); // ‘classpath’ for launching Felix
for (ModuleId id: library.listModuleIds())
{
if (isJreModule(id.name()))
{
urls.add(library.classPath(id).toURI().toURL());
final ModuleInfo info = library.readModuleInfo(id);
appendExportedPackages(info, packages);
}
}
packages.deleteCharAt(packages.length()-1);
final File userDir = new File(System.getProperty(“user.dir”));
// Additional “classpath” (Felix’s jar is added there)
//
addNonOsgiJars(userDir, urls);
// Initialise and start OSGi
//
startOsgi(userDir, urls, packages.toString());
}
catch (IOException | ClassNotFoundException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | IllegalAccessException ex)
{
throw new RuntimeException(“error initialising”, ex);
}
System.out.println(“started.”);
}private static boolean isJreModule(final String moduleName)
{
return moduleName.startsWith(“java”) || moduleName.startsWith(“javax”);
}
private static void appendExportedPackages(final ModuleInfo info, final StringBuilder packages)
{
final ModuleView view = info.defaultView();
for (String exportedPackage: view.exports())
{
if (!isJavaPackage(exportedPackage))
{
packages.append(exportedPackage).append(’;’);
}
}
}
private static boolean isJavaPackage(final String packageName)
{
// java.* are always available to bundles
return packageName.startsWith(“java.”);
}
private static void addNonOsgiJars(final File userDir, final List<URL> urls) throws MalformedURLException
{
final File bundlesDir = new File(userDir, “nonbundles”);
for (File file : bundlesDir.listFiles())
{
urls.add(file.toURI().toURL());
}
}
// As Jigsaw currently identificate itself as Java 1.8, and as Felix already has Java 1.8 package, // override the full system packages, including Felix’s own private static final String FELIX_SYSTEM_PACKAGES =
“org.osgi.framework; version=1.7.0,” +
" org.osgi.framework.hooks.bundle; version=1.1.0,” +
" org.osgi.framework.hooks.resolver; version=1.0.0,” +
" org.osgi.framework.hooks.service; version=1.1.0," +
" org.osgi.framework.hooks.weaving; version=1.0.0," +
" org.osgi.framework.launch; version=1.1.0," +
" org.osgi.framework.namespace; version=1.0.0," +
" org.osgi.framework.startlevel; version=1.0.0," +
" org.osgi.framework.wiring; version=1.1.0," +
" org.osgi.resource; version=1.0.0," +
" org.osgi.service.packageadmin; version=1.2.0," +
" org.osgi.service.startlevel; version=1.1.0," +
" org.osgi.service.url; version=1.0.0," +
" org.osgi.util.tracker; version=1.5.1 “;
private static Map<String, Object> getFrameworkProperties(final String packages)
{
final Map config = new HashMap<>();
config.put(“org.osgi.framework.system.packages”, FELIX_SYSTEM_PACKAGES + “,” + packages);
// config.put(“org.osgi.framework.system.packages.extra”, packages);
return config;
}
// we use reflection to not depend on OSGi core (and to not make a module of it)
private static final Class<?>[] NO_ARG_SIG = null;
private static final Object[] NO_ARG_INV = null;
private static void startOsgi(final File userDir, final List<URL> urls, final String packages) throws ClassNotFoundException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IllegalAccessException
{
final URLClassLoader cl = getClassLoaderFor(urls);
final Class<?> bundleClass = cl.loadClass(“org.osgi.framework.Bundle”);
final Class<?> bundleContextClass = cl.loadClass(“org.osgi.framework.BundleContext”);
final Class<?> frameworkFactoryClass = cl.loadClass(“org.osgi.framework.launch.FrameworkFactory”);
final Map<String, Object> properties = getFrameworkProperties(packages);
final Object framework = getFrameworkFor(frameworkFactoryClass, cl, properties);
final Class<?> frameworkClass = framework.getClass();
initialiseFramework(frameworkClass, framework);
final Object bundleContext = getBundleContextFor(frameworkClass, framework);
final List<Object> installedBundles = new ArrayList<>();
installBundles(installedBundles, userDir, bundleContextClass, bundleContext);
startBundles(installedBundles, bundleClass);
startFramework(frameworkClass, framework);
}private static URLClassLoader getClassLoaderFor(final List<URL> urls)
{
final URL[] urlsArray = urls.toArray(new URL[urls.size()]);
return new URLClassLoader(urlsArray);
}private static Object getFrameworkFor(final Class<?> frameworkFactoryClass, final URLClassLoader cl, final Map<String, Object> properties) throws IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, IllegalAccessException
{
final ServiceLoader<?> sl = ServiceLoader.load(frameworkFactoryClass, cl);
final Object factory = sl.iterator().next();
final Method newFramework = factory.getClass().getMethod(“newFramework”, Map.class);
return newFramework.invoke(factory, properties);
}private static Object getBundleContextFor(final Class<?> frameworkClass, final Object framework) throws NoSuchMethodException, IllegalAccessException, SecurityException, IllegalArgumentException, InvocationTargetException
{
final Method getBundleContext = frameworkClass.getDeclaredMethod(“getBundleContext”, NO_ARG_SIG);
final Object bundleContext = getBundleContext.invoke(framework, NO_ARG_INV);
return bundleContext;
}private static void initialiseFramework(final Class<?> frameworkClass, final Object framework) throws InvocationTargetException, SecurityException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException
{
final Method init = frameworkClass.getDeclaredMethod(“init”, NO_ARG_SIG);
init.invoke(framework, NO_ARG_INV);
}
private static void installBundles(final List<Object> installedBundles, final File userDir, final Class<?> bundleContextClass, final Object bundleContext) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
final Method installBundle = bundleContextClass.getDeclaredMethod(“installBundle”, String.class);
final File bundlesDir = new File(userDir, “bundles”);
for (File file : bundlesDir.listFiles())
{
final String name = file.toURI().toString();
final Object bundle = installBundle.invoke(bundleContext, name);
installedBundles.add(bundle);
}
}private static void startBundles(final List<Object> installedBundles, final Class<?> bundleClass) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
final Method start = bundleClass.getDeclaredMethod(“start”, NO_ARG_SIG);
for (Object bundle: installedBundles)
{
start.invoke(bundle, NO_ARG_INV);
}
}private static void startFramework(final Class<?> frameworkClass, final Object framework) throws IllegalAccessException, SecurityException, NoSuchMethodException, InvocationTargetException, IllegalArgumentException
{
final Method start = frameworkClass.getDeclaredMethod(“start”, NO_ARG_SIG);
start.invoke(framework, NO_ARG_INV);
}
}Make
cd ~/dev/felix/felix-launcher/
mvn install
Test with a sample bundle
Create the directories
mkdir -p ~/dev/felix/simplebundle/src/main/java/lh/jigsaw/test/simplebundle
Add the pom
gedit ~/dev/felix/simplebundle/pom.xml &
<project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
<modelVersion>4.0.0</modelVersion><groupId>lh.jigsaw.test</groupId>
<artifactId>simplebundle</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>bundle</packaging><name>simplebundle OSGi Bundle</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>4.3.0</version>
<scope>provided</scope>
</dependency>
</dependencies><build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.7</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-Activator>lh.jigsaw.test.simplebundle.Activator</Bundle-Activator>
<Export-Package/>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>Add the bundle activator
gedit ~/dev/felix/simplebundle/src/main/java/lh/jigsaw/test/simplebundle/Activator.java &
package lh.jigsaw.test.simplebundle;
import javax.swing.JOptionPane;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;public class Activator implements BundleActivator
{
public void start(BundleContext context) throws Exception
{
System.out.println(“Hello Jigsaw/Felix!”);
JOptionPane.showMessageDialog(null, “Hello Jigsaw/Felix!”);
}public void stop(BundleContext context) throws Exception
{
}
}Make
(as the bundle is compiled with a compiler that does not know about Jigsaw, we must use the standard Java 7)export JAVA_HOME=’/usr/lib/jvm/java-7-openjdk-amd64’
cd ~/dev/felix/simplebundle/
mvn installCopy to the bundle directory
cp ~/dev/felix/simplebundle/target/simplebundle-1.0-SNAPSHOT.jar ~/dev/felix/bundles
Run
cd ~/dev/felix/
java -L ~/dev/felix/modules -m lh.jigsaw.felix.launcher