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):
(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.)
<!-- 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>
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!
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Tito Sanchez replied on Tue, 2011/04/26 - 2:26pm
Excellent article.
Now I'm using NSIS to include JRE in my NBP project but I need deploy it also in MAC and Linux.
Javier Ortiz replied on Tue, 2011/04/26 - 7:57pm
in response to:
Tito Sanchez
Mark Clarke replied on Wed, 2011/04/27 - 4:49am
Ernest Lotter replied on Wed, 2011/04/27 - 8:52am
in response to:
Tito Sanchez
Tito Sanchez replied on Thu, 2011/04/28 - 10:11am
in response to:
Ernest Lotter
Hi Ernest.
I will try to create our installer using NB (currently we have some dependencies: mysql and other tools) but looking this article I have some ideas to apply.
Thanks!
Drew Willis replied on Fri, 2011/11/04 - 1:13pm
This all works wonderfully for windows .exe installers. I am experiencing a small problem in Linux. Despite the fact that the self-extracting bundled linux_jvm.sh file extracts itself correctly, when packaged into the application installer, the application installer extracts the jvm to the jre directory but the execute flag for the files in the <apppath>/jre/bin directory have not been set (which is important for /jre/bin/java). Strangely, it allows the installer to run from the bundled jvm but when I attempt to launch the application, it sees the jre directory and the jvm as <apppath>/jre/bin/java but does not execute the jvm as this file does not have it's execute flag set.
I have verified file access privs and validated the self-extracting zip linux_jvm.sh preserves the execute flags. I have also verified that the app runs properly when I go into the <apppath>/jre/bin directory and execute the command "chmod 755" which sets the execute flag for all these files.
Any ideas?
--dreww
Ernest Lotter replied on Thu, 2011/11/24 - 2:39pm
Adam Metzler replied on Sun, 2011/11/27 - 12:17am
Brian McCormick replied on Wed, 2012/05/02 - 9:09am
Ernest Lotter replied on Sat, 2012/06/30 - 8:50am
Hi Brian, hopefully rather late than never, but here goes: I've made self-extracting images for 32-bit and 64-bit Linux and Windows JRE's, then you can modifiy the Linux section of the build.xml above to:
Same trick works for Windows side, and this results in 32-bit and 64-bit installers placed next to each other for each platform.
Jemiah Aitch replied on Sun, 2012/10/07 - 5:48pm
Javier Ortiz replied on Tue, 2012/11/06 - 9:45am
Would be great to have the Mavenized version of this as well!
Brian McCormick replied on Thu, 2012/12/13 - 5:33pm
Ernest,
Thank for the followup on how to make builds for both 32 and 64 bit versions in the installer. I have a new issue very closely related. I have this app written for a very large organization where only administratiors can install applications. Regular users cannot. They want me to give them a deployment with the embedded JREs like the installer but packaged as a zip distribution instead so that they don't have to do a formal install. I am not an ant script expert but I think if I knew which sectionin the ant scripts I should make the additions/changes I might just be able to figure it out.
Any help would be greatly appreciated.
Thanks!
Matt Coleman replied on Thu, 2013/02/21 - 1:18am
in response to:
Javier Ortiz
YES!!for Maven please!!
buffalo freelance web designer
Gary Chen replied on Tue, 2013/05/21 - 4:25am
One question, who will unpack rt.jar.pack.gz?
Winston Dehaney replied on Tue, 2013/05/21 - 1:10pm
Is this the same way to include any folder at all in the installation process? What happened is that I was using the NetBeans Platform Application module to create a desktop application for a class project. I have my pictures in my modules suite folder, but when I try to make an installation file it doesn't use that folder so my application ends up not showing some pictures. So I was wondering if this would automatically include these folders. Don't know if my request is clear.