The Foxfire Blog

Notes from my past self.

View on GitHub
9 January 2019

Embedded JRE for Windows

by Sasha Elaine Fox

Deploying Java Desktop Apps on Windows

So you’ve written your shiny new Java app, and now it’s time to deploy on Windows. Thanks to Java’s platform independence, all we need to do is bundle up our app in a .jar and, assuming the end-user has the Java Runtime Environment (JRE) installed, we are good to go right?

Unfortunately there is no guarantee that the end user will have the JRE installed, nor are we guaranteed that the correct version will be installed if the JRE is present.

We are left with two options: either install the required version of the JRE on the host system, or ship a version of the JRE embedded into our app. If we go with the latter option, we will need a way to make sure that our app knows how to used the embedded JRE.

There’s also the question of Executable files. Windows users are usually expecting (for better or worse) apps to be shipped as self-contained installers and exe files.

Launch4j

Installation and Set-Up

Unfortunately launch4j is not available via chocolatey and needs to be installed from the Launch4j Sourceforge.

By default launch4j is not added to the system Path. To make it accessible via the command line you’ll need to add the launch4j.exe and launch4jc.exe (the command line version of launch4j) folder location to the Path. One way to done this is by modifying your Powershell profile, Microsoft.PowerShell_profile.ps1 and adding the following line.

# Add Launch4J to the PATH.
$env:Path += "C:\Program Files (x86)\Launch4j";

Once launch4j is accessible via the command line, a GUI interface can be launched as follows:

launch4j

This GUI is designed to aid in generating config files. However, config files can ben generated and edited manually if desired.

Once an appropriate config file has been generated the executable wrapper is generated using launch4jc as follows.

launch4jc config.xml

System Wide Java Installation

Launch4J can enforce the use of a system-wide JRE/JDK installation to run an app. Launch4J will build a self-contained executable that will check to see if an appropriate JRE is present on the host system and if one is not found, prevent the program from starting (along with directing the user to a place to download a JRE).

Let’s assume we have a project with following structure.

C:.
│   config.xml
│   ProjectIcon.ico
│   ProjectJar.jar
<?xml version="1.0" encoding="UTF-8"?>
<launch4jConfig>
  <dontWrapJar>false</dontWrapJar>
  <headerType>gui</headerType>
  <jar>ProjectJar.jar</jar>
  <outfile>bin\ProjectExe.exe</outfile>
  <errTitle></errTitle>
  <cmdLine></cmdLine>
  <chdir>.</chdir>
  <priority>normal</priority>
  <downloadUrl>http://java.com/download</downloadUrl>
  <supportUrl></supportUrl>
  <stayAlive>false</stayAlive>
  <restartOnCrash>false</restartOnCrash>
  <manifest></manifest>
  <icon>$ProjectIcon.ico</icon>
  <jre>
    <path></path>
    <bundledJre64Bit>false</bundledJre64Bit>
    <bundledJreAsFallback>false</bundledJreAsFallback>
    <minVersion>1.8</minVersion>
    <maxVersion></maxVersion>
    <jdkPreference>preferJre</jdkPreference>
    <runtimeBits>64/32</runtimeBits>
  </jre>
</launch4jConfig>

Some things to note about the config:

When we run launch4jc config.xml our project will look as follows:

C:.
│   config.xml
│   ProjectIcon.ico
│   ProjectJar.jar
├───bin
│   │   ProjectExe.exe

Running ProjectExe.exe will run our .jar file using the system-wide installation of Java.

Embedded JRE

Lauch4J can also included a self-contained version of the JRE. Launch4J will produce an executable that contains a JRE that will be used whenever our app is run.

First we’ll need to get an embeddable JRE from here.

Let’s assume we have a project structured as follows. Note that jre1.8.0_191 is the root directory of the embedded JRE that will be included with our project.

C:.
│   config.xml
│   ProjectIcon.ico
│   ProjectJar.jar
├───bin
│   │   ProjectExe.exe
|   ├───jre1.8.0_191
|   │   ├───bin
|   │   └───lib
<?xml version="1.0" encoding="UTF-8"?>
<launch4jConfig>
  <dontWrapJar>false</dontWrapJar>
  <headerType>gui</headerType>
  <jar>ProjectJar.jar</jar>
  <outfile>bin\ProjectExe.exe</outfile>
  <errTitle></errTitle>
  <cmdLine></cmdLine>
  <chdir>.</chdir>
  <priority>normal</priority>
  <downloadUrl>http://java.com/download</downloadUrl>
  <supportUrl></supportUrl>
  <stayAlive>false</stayAlive>
  <restartOnCrash>false</restartOnCrash>
  <manifest></manifest>
  <icon>$ProjectIcon.ico</icon>
  <jre>
    <path>jre1.8.0_191</path>
    <bundledJre64Bit>true</bundledJre64Bit>
    <bundledJreAsFallback>false</bundledJreAsFallback>
    <minVersion>1.8</minVersion>
    <maxVersion></maxVersion>
    <jdkPreference>jdkOnly</jdkPreference>
    <runtimeBits>64/32</runtimeBits>
  </jre>
</launch4jConfig>

Note that the major change is <bundledJre64Bit> being set to true. This tells Launch4J that we should use the JRE specified in <path> instead of a system-wide JRE installation.

Note that the path to the JRE is relative to where ever the project executable is, ($PROJECT_EXE_DIR in or example) not the jar file, or the chdir path.

tags: java - deployment - windows