Platinum Partner
netbeans

Including the JRE in a NetBeans Platform Installer on Ubuntu Linux

The NetBeans Platform provides a very nicely integrated installer infrastructure (NBI) which alllows for creating native installers for various platforms (Linux and Windows, in particular). This does, in a cross-platform way, what various other installers do (NSIS, IzPack, etc), but with the advantage of being NetBeans Platform aware and completely configurable.

It is not immediately clear how to bundle a JVM because using the default "Package As | Installers" option in NetBeans IDE 7.0 will build installers that are dependent on an already installed JVM on the system. In this guide, with help from Geertjan and Dmitry, we aim to configure the build process so that:

  • Native installers will be built for (at least) Windows and Linux.

  • These native installers will have a bundled JVM, and thus not require the system they are installed on to have a pre-installed JVM.

  • Ensure that the installed NetBeans Platform application uses a private JRE (in fact, the same one that was bundled before) which is therefore not visible to other applications in the system.

Getting a nice installer cycle with private JRE under Ubuntu Linux (Windows, very similar), to build both Windows and Linux installers from within Ubuntu.

Create JRE's to bundle

# Creating a directory where I will keep the JREs to bundle in various installers,
mkdir ~/bundled_jres
cd ~/bundled_jres

# 1. Set up Linux JRE
cp -r /some/path/to/linux/jre linux_jre (if using Windows, use xcopy /E or Windows Explorer)
cd linux_jre
# pack200 it
pack200 -J-Xmx1024m lib/rt.jar.pack.gz lib/rt.jar
# zip it
zip -9 -r -y ../linux_jvm.zip .
cd ..
# get unzipsfx for Linux
cp /usr/bin/unzipsfx unzipsfx.bin
# concatenate sfx header with zip payload, this results in a self-extracting jvm executable
cat unzipsfx.bin linux_jvm.zip > linux_jvm.bin (if using Windows, use copy /B unzipsfx.bin + linux_jvm.zip linux_jvm.bin)

# 2. Set up Windows JRE
cp -r /some/path/to/windows/jre windows_jre (if using Windows, use xcopy /E or Windows Explorer)
cd windows_jre
# pack200 it
pack200 -J-Xmx1024m lib/rt.jar.pack.gz lib/rt.jar
# zip it
zip -9 -r -y ../windows_jvm.zip .
cd ..
# get unzipsfx.exe for Windows, can find in ftp://ftp.info-zip.org/pub/infozip/win32/unz600xn.exe
cp /path/to/unzipsfx.exe unzipsfx.exe
# concatenate sfx header with zip payload, this results in a self-extracting jvm executable (Windows)
cat unzipsfx.exe windows_jvm.zip > windows_jvm.exe (for Windows, use copy /B as above)

At this point, we have a set of JVMs (not necessarily only Windows and Linux) which we can re-use for perpetuity, or update when significant new releases of the JRE becomes available.

Set up build.xml for NBI

Now, open <netbeans-install-dir>/harness/nbi/stub/build.xml :

Search for "-generate-bundles" target and replace the <create-bundle> call by the following set of conditional calls (changing /home/ernest to rather be your home dir of choice):


<!-- Linux installer -->
<if property="platform" value="linux">
<create-bundle root="${output.dir}/registry-temp" platform="${platform}"
target="${bundles.release.dir}/${bundle.files.prefix}-${platform}.${bundle.extention}">
<component uid="${main.product.uid}" version="1.0.0.0.0"/>
<property name="nbi.bundled.jvm.file" value="/home/ernest/bundled_jres/linux_jvm.bin"/>
</create-bundle>
</if>

<!-- Solaris installer -->
<if property="platform" value="solaris">
<create-bundle root="${output.dir}/registry-temp" platform="${platform}"
target="${bundles.release.dir}/${bundle.files.prefix}-${platform}.${bundle.extention}">
<component uid="${main.product.uid}" version="1.0.0.0.0"/>
<property name="nbi.bundled.jvm.file" value="/home/ernest/bundled_jres/linux_jvm.bin"/>
</create-bundle>
</if>

<!-- Windows installer -->
<if property="platform" value="windows">
<create-bundle root="${output.dir}/registry-temp" platform="${platform}"
target="${bundles.release.dir}/${bundle.files.prefix}-${platform}.${bundle.extention}">
<component uid="${main.product.uid}" version="1.0.0.0.0"/>
<property name="nbi.bundled.jvm.file" value="/home/ernest/bundled_jres/windows_jvm.exe"/>
</create-bundle>
</if>

<!-- Mac installer -->
<if property="platform" value="macosx">
<create-bundle root="${output.dir}/registry-temp" platform="${platform}"
target="${bundles.release.dir}/${bundle.files.prefix}-${platform}.${bundle.extention}">
<component uid="${main.product.uid}" version="1.0.0.0.0"/>
</create-bundle>
</if>
(In the above, one could have multiple JVM's for multiple platforms, and just hook them up in the correct way as here. For example, in the Mac installer above we don't bundle any JVM.)

After doing this, generated installers should run off their bundled JVM's, but the application itself will not use this JVM, and search for a system JVM. To rather let it depend on this private JRE, continue …

Letting the application depend on the private JVM

Now, we edit ConfigurationLogic.java in <netbeans-install-dir>/harness/nbi/stub/ext/components/products/helloworld/src/org/mycompany/ConfigurationLogic.java to (a) extract the previously bundled JRE to a subdirectory called “jre” upon installation and (b) ensure it is also removed when the application gets uninstalled.

Add import statement:
import org.netbeans.installer.utils.system.launchers.LauncherResource;

At the end of install(), add:
File javaHome = new File(System.getProperty("java.home"));
File target = new File(installLocation, "jre");
try {
    FileUtils.copyFile(javaHome, target, true); //FileUtils is one of the NBI core classes, already imported in ConfigurationLogic.java
} catch (IOException e) {
    throw new InstallationException("Cannot copy JRE",e);
}
       
// set permissions:
File binDir = new File(target, "bin");
for (File file : binDir.listFiles()) {
    try {
            file.setExecutable(true);
    } catch (Exception ex) { ex.printStackTrace(); }
}
       
// to add uninstaller logic:
SystemUtils.getNativeUtils().addUninstallerJVM(new LauncherResource(false, target));

and at the end of uninstall(), but just before progress.setPercentage(Progress.COMPLETE);, add
File jre = new File(installLocation, "jre");
if (jre.exists()) {
    try {
        for (File file : FileUtils.listFiles(jre).toList()) {
            FileUtils.deleteOnExit(file);
        }
        FileUtils.deleteOnExit(installLocation);
    } catch (IOException e) {
        //ignore
    }
}

Hook up a custom application configuration file, following Geertjan's http://blogs.sun.com/geertjan/entry/support_for_custom_configuration_files approach, but ensure that my.conf contains the line
jdkhome="jre"
which will be a relative path to the JRE that was installed by ConfigurationLogic's install() method.

Finally, to create our set of installers for various platforms and their respective bundled JRE's, right-click on your suite application, select "Package As", then choose "Installers". To create more compact installers (compressing .jar files using pack200), you can enable this by right-clicking, selecting Properties, select "Installer" and check "Use pack200 compression" - this has the effect that building the installers takes quite a bit longer due to the pack200 compression, but your users will be quite happy to have significantly smaller installers to download.

Many thanks to Geertjan Wielenga and Dmitry Lipin (the NetBeans Installer guru) without whom finding all the necessary ingredients would not have been possible!

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}