Thursday, 14 May 2015

Building Java 9 on Windows with Visual Studio 2013 Community

As the JDK moves to Visual Studio 2013 for its toolchain, this version focuses on building Java 9 on Windows (10 64bits) with Visual Studio 2013 (Community). As it takes a few short-cuts, refer to the Java 9 with plain Windows SDK version or/and to the Java 8 version (for using plain Cygwin).

  1. Create a C:\dev\ directory
  2. Download and install Visual Studio 2013 Community
    1. Download (it may be faster to download the ISO)
    2. Install
      • uncheck all the options
      • or download AdminDeployment.xml to C:\dev\temp then start the installation with:
        vs_community.exe /AdminFile C:\dev\temp\AdminDeployment.xml
    3. Add the path to MSBuild to the PATH environement variable (required to build Freetype):
      C:\Program Files (x86)\MSBuild\12.0\Bin
  3. Download and install the Mercurial for Windows 
  4. Download and install Java 8 (if not already done)
  5. Download and install Babun, then use its package manager to install zip :
    pact install zip
  6. Download Freetype source (2.5.5) and extract to C:\dev\freetype-2.5.5
  7. In the Babun console:
    cd /cygdrive/c/dev
  8. Get the root source:
    hg clone http://hg.openjdk.java.net/jdk9/jdk9/
     (or the more bleeding edge http://hg.openjdk.java.net/jdk9/dev/ )
  9. Move to the new directory
    cd /cygdrive/c/dev/jdk9
  10. Get the remainder of the source
    ./get_source.sh 
  11. Force the permissions
    chmod -R u+rwxs .
    (otherwise some files get access denied errors)
  12. Run the auto-conf script
    bash configure --with-freetype-src=/cygdrive/c/dev/freetype-2.5.5
  13. Time for cooking
    make images
  14. Once done, open a new standard Windows console and navigate to
    C:\dev\jdk9\build\windows-x86_64-normal-server-release\images\jdk
    check that java runs:
    bin\java -version
    or for something a bit more visual:
    bin\jconsole
  15. enjoy!
For day to day refresh do the following (in the Babun shell):
  1. Go to the JDK directory
    cd /cygdrive/c/dev/jdk9
  2. Get the sources updates
    ./get_source.sh
  3. Force the permissions
    chmod -R u+rwxs .
  4. Remove the build directory
    rm -rf build/
  5. Rerun the auto-conf script to update the time stamp of the build:
    bash configure --with-freetype-src=/cygdrive/c/dev/freetype-2.5.5
  6. Re-heat
    make images

Thursday, 5 February 2015

Using JDK9/Jigsaw-m2 for Netbeans projects

Last updated: 2015-12-06, nightly builds (aka Development builds) now support the current JDK 9, so this post is no longer of any use.

When Jigsaw/m2 appeared on the OpenJDK, I tried it with my usual guinea pig, NetBeans. Launching was fine, however using Jigsaw as the platform for a project was not due to the new image (JEP 220). In due time it will be supported but currently it isn't. So I set out to see if I could not add some support myself.
Since I first wrote this post the NetBeans team started their work for supporting JDK 9, check the NetBeans JDK 9 support wiki page for more.

For now the following seem to work for me:
  • define a JDK 9 platform
  • class indexation and autocompletion
  • run
I tested with an own build jdk9/jdk9 (so it should also work with a recent JDK9 early access build)

To try with your own NetBeans build:
  1. Download the diff (view)
  2. Import it with Mercurial
To run NetBeans with JDK 9 yet develop for it, add the following lines to <nb working copy>/nbbuild/user.build.properties :
nbjdk.home=C:\\Program Files\\Java\\jdk1.7.0_76
# if nbjdk.home is pointing to a JDK8 then uncomment:
#permit.jdk8.builds=true
To launch an instance of NetBeans running JDK 9 via NetBeans, in <nb working copy>/nbbuild/build.xml, for the tryme target, change the value of the <arg file="${nbjdk.home}"/> of the exec task to the path to JDK9

Explore!

Saturday, 19 July 2014

Building OpenJFX on Windows

This post is a step by step to build OpenJFX on Windows (8.1 64bits). It is inspired from the official instructions though it drops Visual Studio Express (which does not come with 64bits compiler and linker) in favour of plain SDK. It also uses Babun rather than plain Cygwin.

  1. Create a C:\dev directory
  2. Download and install a Java 8 JDK (I'm using 8u20 here)
  3. Download Gradle 1.8 and extract it into C:\dev
  4. Download and install Windows SDK 7.1 (no need for the samples or .Net tools)
  5. The SDK installer has a small issue in that it doesn't create a registry key that is needed by the batch files that set up the environment variables...
    Copy and paste the following into a vs.reg file:
    Windows Registry Editor Version 5.00

    [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7]
    "10.0"="C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\"
    once saved, double click on the file to add the registry key.
  6. Download and install Microsoft DirectX 9.0 SDK (from June 2010)
  7. Download Babun, unpack into a temporary directory, open a console in the babun directory and type:
    install /target C:\dev
  8. Download and install Mercurial
  9. In C:\dev open a standard console
  10. Get the sources:
    In the console copy:
    hg clone http://hg.openjdk.java.net/openjfx/8u-dev/rt jfx
  11. Set up some environment variables:
    Still in the console:
    set GRADLE_HOME=C:\dev\gradle-1.8
    set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_20
    set PATH=%PATH%;%JAVA_HOME%\bin;%GRADLE_HOME%\bin;C:\dev\.babun\cygwin\bin
    set WINSDK_DIR=C:\Program Files\Microsoft SDKs\Windows\v7.1
    (The later indicating to use the Windows 7.1 SDK for includes and libs)

  12. Build:
    Still in the console:
    cd jfx
    gradle
  13. Open an admin console (search for cmd then right click then run as admin), then run the following to update the JavaFX files in the JDK:

    copy C:\dev\jfx\build\sdk\bin\*.* "C:\Program Files\Java\jdk1.8.0_20\bin"
    copy C:\dev\jfx\build\sdk\lib\*.* "C:\Program Files\Java\jdk1.8.0_20\lib"

    copy C:\dev\jfx\build\sdk\rt\bin\*.* "C:\Program Files\Java\jdk1.8.0_20\jre\bin"
    copy C:\dev\jfx\build\sdk\rt\lib\*.* "C:\Program Files\Java\jdk1.8.0_20\jre\lib"
    copy C:\dev\jfx\build\sdk\rt\lib\ext\*.* "C:\Program Files\Java\jdk1.8.0_20\jre\lib\ext"
  14. In the console that built OpenJFX:
    gradle sampleAppsJar -Dplatforms.JDK_1.8.home="%JAVA_HOME%"
    then
    "%JAVA_HOME%\bin\java" -jar C:\dev\jfx\apps\samples\Ensemble8\dist\Ensemble8.jar
  15. Hack...

Using Babun seems to take care of the Cygwin packages that are needed by OpenJFX, those that may not be already installed may be added using:
pact install  <package name>
(an exception being g++: mingw64-x86_64-gcc-g++ )

Sunday, 1 June 2014

Building Java 9 on Windows

This version focuses on building Java 9 on Windows (8.1 64bits) and takes a few short-cuts from the Java 8 version (refer to that version if you get issues).

Last updated: 2015-05-14, fixed JDK image path, Freetype 2.5.5, use cygpaths

  1. Download and install Windows SDK 7.1 (there is no need for the samples and .Net tools)
  2. Download and install Microsoft DirectX 9.0 SDK (the ReadMe says the version from summer 2004, however this does not seem to still be available from Microsoft, the version from June 2010 seems to be a working replacement)
  3. Download and install the Mercurial for Windows
  4. Download and install Java 8 (if not already done)
  5. Download and install Babun, then use its package manager to install zip :
    pact install zip
  6. Create a C:\dev\ directory 
  7. Download Freetype source (2.5.5) and extract to C:\dev\freetype-2.5.5
  8. In the Babun console:
    cd /cygdrive/c/dev/jdk9
    (use a slash / instead of a back-slash \ )
  9. Get the root source:
    hg clone http://hg.openjdk.java.net/jdk9/jdk9/
     (or the more bleeding edge http://hg.openjdk.java.net/jdk9/dev/ )
  10. Move to the new directory
    cd /cygdrive/c/dev/jdk9
  11. Get the remainder of the source
    ./get_source.sh 
  12. Force the permissions
    chmod -R u+rwxs .
    (otherwise some files get access denied errors)
  13. Run the auto-conf script
    bash configure --with-freetype-src=/cygdrive/c/dev/freetype-2.5.5
  14. Time for cooking
    make images
  15. Once done, open a new standard Windows console and navigate to
    C:\dev\jdk9\build\windows-x86_64-normal-server-release\images\jdk
    check that java runs:
    bin\java -version
    or for something a bit more visual:
    bin\jconsole
  16. enjoy!
For day to day refresh do the following (in the Babun shell):
  1. Go to the JDK directory
    cd /cygdrive/c/dev/jdk9
  2. Get the sources updates
    ./get_source.sh
  3. Force the permissions
    chmod -R u+rwxs .
  4. Remove the build directory
    rm -rf build/
  5. Rerun the auto-conf script to update the time stamp of the build:
    bash configure --with-freetype-src=/cygdrive/c/dev/freetype-2.5.5
  6. Re-heat
    make images

Wednesday, 29 January 2014

Hacking NetBeans: Improving the Manage Groups dialog

In my previous post I tried to show how I'd like to improve NetBeans Manage Groups dialog, in this one I'll try to show how to.

Setting up:
  1. Get and install Mercurial
  2. Clone http://hg.netbeans.org/main
  3. Get Ant (1.9.3 worked for me)
  4. Make sure you have Java 7 (8 doesn't work)
  5. Open a shell in your working copy directory to do a first build that's needed for creating Ant tasks that are used by the NetBeans projects
    set PATH=%PATH%;C:\softs\apache-ant-1.9.3\bin
    set JAVA_HOME=C:\Program Files\Java\jdk1.7.0_51
    set ANT_OPTS=-Xmx512m -XX:MaxPermSize=512m
    ant

With the first build done it is time to have a look at the working copy. Looking at the long list of modules, the first one that seems to talk about project and ui is projectui. A quick drill down shows that it also talks about groups, so lets open it in NetBeans.

org.netbeans.modules.project.ui.groups has a ManageGroupsPanel that correspond to our dialog (panel).

It doesn't include the bottom buttons though. Searching the nearby classes the buttons are defined in GroupsMenu, in manageGroups() on line 196:
 dd.setOptions(new Object[] {select, newGroup, cancel});
Lets remove newGroup from that line, then run the project then in the new NetBeans instance open the dialog, the button is gone.

Now coming back to the main dialog, the first thing that distracted me was the height of the buttons - 29 -, a quick look at other another dialog with a button shows a height of 23, so adjust the height of the buttons to 23 via the designer.

Run again, it looks nicer.

Next readding the New button. Drop a button above the Properties one... The panel is using a GrigBagLayout (long time no see), so adjust the Grid X/Y properties of the buttons to reallign (2,1 for New, 2,2 for Properties,...)
Next set the text of the New button, to do it click on the "..." by the text property then fill as:

Switch to the code tab of the properties, name the variable newButton.
Switch to Events, add a handler for actionPerformed (newButtonActionPerformed).
Now we need to add the action. The simplest is to move over the code from GroupsMenu.newGroup() along with the HELPCTX constant, adjust the messages prefix to ManageGroupsPanel instead of GroupsMenu, then call from the handler.

Run again, click new, click Create Group the new group is created and selected but not to the list...

To do that, first make the newGroup method non static, then in the
 RP.post(new Runnable() {
block add after
Group.setActiveGroup(g, true);
the following
String selectedValue = null;
DefaultListModel model = (DefaultListModel)groupList.getModel();
model.removeAllElements();
for (final Group grp : Group.allGroups()) {
    model.addElement(grp.getName());
    if(grp.equals(Group.getActiveGroup())) {
        selectedValue = grp.getName();
    }
}
model.addElement(NONE_GROUP);
groupList.setSelectedValue(selectedValue, true);
Run again, now the list is updated.

The old dialog was closing both the New Group and the Manage Groups dialogs.
To give that option lets add a new button "Create and Select".
First add a new message:
"ManageGroupsPanel.new_create_and_select=Create and Select",
between create and cancel.
Then after the cancel button creation:
final JButton createAndSwitch = new JButton(ManageGroupsPanel_new_create_and_select());
Then change the line that follows to:
dd.setOptions(new Object[] {create, createAndSwitch, cancel});

Then allow the if(result...) condition to also handle createAndSwitch:
if (result.equals(create) || result.equals(createAndSwitch)) {
Back up a little and mark result as final

Then after groupList.setSelectedValue(..) add:
if (result.equals(createAndSwitch))
{
        final Window w = SwingUtilities.getWindowAncestor(ManageGroupsPanel.this);
        if (w != null) {
            w.setVisible(false);
            w.dispose();
        }
}
    }
});
Run again, does the trick.

One last thing, the selected group also change when we just click on Select, so let not change the active group in that case:
if (result.equals(createAndSwitch))
{
  Group.setActiveGroup(g, true);
}
The full method:
@Messages({
    "ManageGroupsPanel.new_title=Create New Group",
    "ManageGroupsPanel.new_create=Create Group",
    "ManageGroupsPanel.new_create_and_select=Create and Select",
    "ManageGroupsPanel.new_cancel=Cancel"
})
private void newGroup() {
    final NewGroupPanel panel = new NewGroupPanel();
    DialogDescriptor dd = new DialogDescriptor(panel, ManageGroupsPanel_new_title());
    panel.setNotificationLineSupport(dd.createNotificationLineSupport());
    dd.setOptionType(NotifyDescriptor.OK_CANCEL_OPTION);
    dd.setModal(true);
    dd.setHelpCtx(new HelpCtx(HELPCTX));
    final JButton create = new JButton(ManageGroupsPanel_new_create());
    create.setDefaultCapable(true);
    create.setEnabled(panel.isReady());
    panel.addPropertyChangeListener(new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if (NewGroupPanel.PROP_READY.equals(evt.getPropertyName())) {
                create.setEnabled(panel.isReady());
            }
        }
    });
    JButton cancel = new JButton(ManageGroupsPanel_new_cancel());
    final JButton createAndSwitch = new JButton(ManageGroupsPanel_new_create_and_select());
    dd.setOptions(new Object[] {create, createAndSwitch, cancel});
    final Object result = DialogDisplayer.getDefault().notify(dd);
    if (result.equals(create) || result.equals(createAndSwitch)) {
        assert panel.isReady();
        final NewGroupPanel.Type type = panel.getSelectedType();
        final boolean autoSync = panel.isAutoSyncField();
        final boolean useOpen = panel.isUseOpenedField();
        final String name = panel.getNameField();
        final String masterProject = panel.getMasterProjectField();
        final String directory = panel.getDirectoryField();
        RP.post(new Runnable() {
            @Override
            public void run() {
                Group g = NewGroupPanel.create(type, name, autoSync, useOpen, masterProject, directory);
                if (result.equals(createAndSwitch))
                {
                  Group.setActiveGroup(g, true);
                }

                String selectedValue = null;
                DefaultListModel model = (DefaultListModel)groupList.getModel();
                model.removeAllElements();
                for (final Group grp : Group.allGroups()) {
                    model.addElement(grp.getName());
                    if(grp.equals(Group.getActiveGroup())) {
                        selectedValue = grp.getName();
                    }
                }
                model.addElement(NONE_GROUP);
                groupList.setSelectedValue(selectedValue, true);
   
                if (result.equals(createAndSwitch))
                {
                        final Window w = SwingUtilities.getWindowAncestor(ManageGroupsPanel.this);
                        if (w != null) {
                            w.setVisible(false);
                            w.dispose();
                        }
                }
                    }
                });
    }
}

Voila.



Projects groups in NetBeans

One of the features of NetBeans that I use the most is the Projects Groups. It allows to arbitrarily group projects together then switch from one group to another. For instance as below, projects related to a lambda lab, project related to the Yoko Tsuno site I help maintaining in a Yoko group, and the default (none) group. Or to switch between a feature development group and a bug fix group.

NetBeans also allows to create a group populated with a project and its required projects, or from all the projects in a directory.

Up to NetBeans 7.4 this was contained in a sub menu of the project context menu:


Fairly fast, but may be not so easy when dealing with a large number of project groups.

So in NetBeans 8 Beta this is changed to a full dialog:


Making it easier to remove several or all groups.
(A nice hidden feature of that dialog is that double clicking a group switches to it)

However I feel, in addition to the buttons being too high, that the "New Group..." button could be moved to the right buttons list:




In NetBeans 8 Beta, the "Create New Group" dialog creates a new group and switch to it, dismissing the "Manage Groups" dialog along the way:


I also think it should offer the possibility to either create a new group and switch to it, but also to create a new group and return to the "Manage Group" dialog to allow for creating more groups:


(though "Create Group" would now just return to the "Manage Group" dialog)

Let's see how in the next post...

Tuesday, 20 August 2013

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:

  1. Build Jigsaw
  2. Build the Maven plugin for Jigsaw 
  3. Download and extract Maven 3(.0.5) in ~/dev
  4. Export the variables:
    export M2_HOME=~/dev/apache-maven-3.0.5/
    export MAVEN_HOME=~/dev/apache-maven-3.0.5/
    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
  1.  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
  2. Create the modules library
    jmod create -L ~/dev/felix/modules
  3. 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)
  4. 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
  5. 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)
  6. 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);
      }
    }
  7. Make
    cd ~/dev/felix/felix-launcher/
    mvn install
     
 Test with a sample bundle
  1. Create the directories
    mkdir -p ~/dev/felix/simplebundle/src/main/java/lh/jigsaw/test/simplebundle
  2.  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>
  3. 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
      {
      }
    }
  4. 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 install

  5. Copy 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