Ernest has posted 2 posts at DZone. View Full User Profile

Including the JRE in a NetBeans Platform Installer on Ubuntu Linux

04.26.2011
| 13765 views |
  • submit to reddit

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!

Published at DZone with permission of its author, Ernest Lotter.

(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

Added a FAQ entry pointing to this? If not you should!

Mark Clarke replied on Wed, 2011/04/27 - 4:49am

thanks for the info. Good article Ernest

Ernest Lotter replied on Wed, 2011/04/27 - 8:52am in response to: Tito Sanchez

Hi Tito Thank you very much. You could experiment with the approach in the article (foregoing NSIS) to build your installers for all platforms, as a major advantage I've found is that this allows you to build installers for all your target platforms from your own machine, while NSIS would only do this for Windows. Thus, my excitement wrt this solution is that it is now possible to build all my installers in one go, and even potentially automate it with an ant task, making it possible to do this in the Hudson context. E

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

@dreww: sorry for replying to you only now, I didn't see this and was luckily reminded of your question by a colleague. I've edited the post above to include a fix to the executable bit issue, see the change to the end of the install() method in ConfigurationLogic.java. Hope this helps!

Adam Metzler replied on Sun, 2011/11/27 - 12:17am

This is great, but, I can't seem to find zip distributions of the jre on the oracle site for any platform other than solaris so this approach is only good for the platform your working on. I know Sun use to provide zip distributions for all platforms so where would one get them now?

Brian McCormick replied on Wed, 2012/05/02 - 9:09am

So this is pretty good but it still leaves one pretty big unanswered question. There are 2 JREs for Windows. A 32 bit and a 64 bit one. Depending on the version of Windows I want to run different versions of the JRE for my Netbeans app. The app I am currently doing manipulates potentially large amounts of spatial data. Using the 32 bit platform can be useful for a lot of the datasets a user may have but for really large datasets the 64 bit JRE is a necessity. Ideally I would create a single Windows installer that contained both a 32 and a 64 bit JRE and the installer would install the correct one depending on the value of, say, the java system property 'os.arch'. The next best thing would be to have the ability to generate a different Windows installer for the 32 and 64 bit JRE versions and then the user could pick the installer they wanted to use. If anyone has some insight to this I would appreciate it.

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:

 

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

 

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

This works great! Thanks Ernest, it was well written. I've been looking for how to do this for a while though I'm late to the party. I just tried it with Netbeans 7.1.1 and 7.2 for Windows using JRE 7u7. No problems. With Java7 available for OSX now I've already had trouble with a machines not having the latest Java update. It looks like the Linux method might work on OSX.... Has anyone tried it? Thanks again. .

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.

Winston Dehaney replied on Mon, 2013/05/27 - 9:30am

OK, guess I'll seee if it's possible

Cata Nic replied on Mon, 2013/09/02 - 4:27am

 The archive must be decompressed? I think is a good solution if the process can be explained in a better way.

Enrico Scantamburlo replied on Thu, 2013/12/19 - 7:03am

Ciao and thanks for the tutorial . I took a look also on the NetBeans wiki and they say that I cannot pack some jars in my installer, like jce.jar, http://wiki.netbeans.org/NBIBundledJVM .

I really need those files for my application. Do you know something about that? 

Kazi Imran Aziz replied on Wed, 2014/09/17 - 9:45am in response to: Matt Coleman

Recently, I also needed to build an installer with bundled JRE based on maven. After some digging, I found the solution to it. I I am trying to post it here as short as possible. For Ant build scripts, the build.xml inside <netbeans-install-dir>/harness/nbi/stub/ is your point of interest. But for Maven build scripts, you need to switch your attention to another file, the template.xml file, which is also under <netbeans-install-dir>/harness/nbi/stub/. Copy the template.xml file somewhere(ex-${basedir}/installer/template.xml ). Inside the new template.xml file, look for the target "prepare-sources"<target name="prepare-sources">, you have to add 2 copy tasks inside this target as shown here.

<copy file="${configuration.logic.file}"  overwrite="true"
              tofile="${installer.build.dir}/ext/components/products/helloworld/src/org/mycompany/ConfigurationLogic.java"/>
        
        <copy file="${nbi.stub.buildfile}"  overwrite="true"
              tofile="${installer.build.dir}/build.xml"/>


Please make sure, this is the 3rd copy task(the first 2 copy tasks copy the required files from the <netbeans-install-dir>/harness/nbi/stub/ to your installer build directory.) The build.xml and ConfigurationLogic.java referenced here are the same files referenced in the main article.
Now,you have to make your pom file, inside the application module, refer to  the new template.xml file.You have to change your Plugin tag with  the following chunk of code-

 <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>nbm-maven-plugin</artifactId>              
                <configuration>
                    <templateFile>${basedir}/installer/template.xml</templateFile>
                    <userSettings>
                        <configuration.logic.file>${basedir}/installer/ConfigurationLogic.java</configuration.logic.file>
                        <nbi.stub.buildfile>${basedir}/installer/build.xml</nbi.stub.buildfile>
                    </userSettings>                    
                    <etcConfFile>src/main/resources/my.conf</etcConfFile>
                    <installerOsSolaris>false</installerOsSolaris>
                    <installerOsMacosx>false</installerOsMacosx>
                    <installerOsLinux>false</installerOsLinux>
                    <installerPack200Enable>false</installerPack200Enable>
                </configuration>
            </plugin>

What is basically happening here, you are declaring your custom template.xml file and inside that file, your instructing, the nbm maven installer to take your own instance of build.xml and ConfigurationLogic.java. Hopefully, these helps to all those looking for a mavenized version of the build.xml file.



Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.