PackagingADistributableJavaApp

Setting up Netbeans to Generate a Distributable JAR File Using OneJAR

By rkusterer AT netbeans DOT org

Contents


Motivation

What's the goal? This tutorial shows how to set up the NetBeans IDE to automate the process of creating one distributable JAR file using OneJAR. In the development phase it suffices to run your Java application from the IDE, but for a release you need a stand-alone distribution--optimally in one user-friendly file. Here we use OneJAR as a way to package Java classes, JAR libraries, as well as native dynamic libraries(!), all in one JAR file.

What's the added value? Although NetBeans creates a JAR file in the dist directory, it is not a stand-alone JAR, because it depends on the libraries in the dist/lib (and dist/natives) directories. This means if your users move the JAR, or lose the dist/lib (or dist/natives) directory, or don't know how to use command line, then they cannot run the JAR. We want to prevent these sources of error and make the distribution more user-friendly using OneJAR.

Is this for you? Not all Java applications depend on native dynamic libraries (.dll, .so, .jnilib/.dylib files). For projects depending on natives, platform-dependent packaging is an important usability factor, since users don't need to install any extra libraries to run the application. If your application does not depend on natives, follow this NetBeans Single JAR Packaging tutorial instead. For Maven, find additional tips in Binkley's blog.


File Structure

The tutorial assumes you have some application already set up and running in the IDE, including all libraries. You can use this File:Test PackagingADistributableJavaApp.zip sample project with native dependencies. I use the following file names, paths, and packages--adjust them accordingly for your project.

  • The Java application to be distributed is HelloWorld. Its main class is my.stuff.HelloWorld.
  • SharedLibraries is a shared directory containing JARs and native libraries that HelloWorld depends on.
  • NetBeans automatically creates the dist directory when you build the project. It contains dist/HelloWorld.jar which depends on the dist/lib directory, but it is lacking the native libs.
  • We will make NetBeans create the dist/natives directory.
  • We will make NetBeans create a stand-alone OneJAR file HelloWorld-all.jar.
  • We will make NetBeans create one distributable ZIP file for each operating system in the releases directory.

This is an overview of the final directory structure we aim to create in this tutorial.

NetBeansProjects/
  BundlingTools/
    one-jar-ant-task-0.96.jar
  SharedLibraries/           (Stuff your project depends on)
      lib/                   (original JAR libraries)
        something-1.jar 
        something-2.jar 
        natives/             (original native libraries)
          something.dll
          libsomething-linux.so
          libsomething.jnilib
  HelloWorld/                (Your project)
    src/my/stuff/HelloWorld.java
    build/classes/my/stuff/HelloWorld.class
    build.xml
    one-jar-ant-task.xml
    one-jar.mf
    dist/
      HelloWorld-all.jar     (custom-created OneJAR)
      HelloWorld.jar         (NetBeans-created JAR)
      lib/                   (copies of JAR libraries)
        something-1.jar 
        something-2.jar 
      natives/               (copies of native libraries)
        something.dll
        libsomething-linux.so
        libsomething.jnilib
    release                  (Goal: Distributable files for each OS)
      HelloWorld-linux.zip
      HelloWorld-mac.zip
      HelloWorld-win.zip


OneJAR

Installing OneJAR

  1. Download one-jar-sdk-096.jar.zip, unpack it somewhere. For version 0.97, download one-jar-ant-task-0.97.jar, rename it to one-jar-ant-task-0.97.zip, and unzip that.
  2. Create a directory BundlingTools next to the HelloWorld directory.
  3. Move the file one-jar-ant-task-0.96.jar from the one-jar-sdk directory into BundlingTools.
  4. Move the file one-jar-ant-task.xml from the one-jar-sdk directory (or from one-jar-ant-task-0.97.zip) into HelloWorld.
  5. Change the first line in one-jar-ant-task.xml to point to your value="../BundlingTools" directory

Adding the OneJAR Manifest

Create a file onejar.mf in the HelloWorld directory, with the following content:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Main-Class: com.simontuffs.onejar.Boot
Class-Path: lib/something-1.jar lib/something-2.jar
One-Jar-Main-Class: my.stuff.HelloWorld
  1. Leave the Main-Class line as is.
  2. Change the Class-Path from lib/something.jar lib/something-2.jar etc to a list of all JARs that your project depends on.
  3. Change the One-Jar-Main-Class attribute to your project's main class.

Adding the OneJAR Ant Task

In NetBeans, open the Files Window and find your project's build.xml script. Add the following Ant target before the closing </project> tag:

<!-- provide a path where executable distros should be saved  -->
<property name="release.dir" value="${basedir}/release"/>
<!-- provide the path to the shared native libraries directory -->
<property name="natives.dir" value="../SharedLibraries/lib/natives/"/>
<!-- provide a base name for your executable. -->
<property name="standalone.jar.name" value="HelloWorld"/>

    <import file="one-jar-ant-task.xml" />
    <target name="dist-all" depends="jar"
            description="Bundles a stand-alone OneJAR distribution">
        <echo>Bundling: OneJAR is copying natives to ${dist.jar.dir}/natives </echo>
        <copy todir="${dist.jar.dir}/natives/linux">
            <fileset dir="${natives.dir}"  includes="*.so"/>
        </copy>
        <copy todir="${dist.jar.dir}/natives/win">
            <fileset dir="${natives.dir}"  includes="*.dll"/>
        </copy>
        <copy todir="${dist.jar.dir}/natives/mac">
            <fileset dir="${natives.dir}"  includes="*.dylib"/>
            <fileset dir="${natives.dir}"  includes="*.jnilib"/>
        </copy>
        <echo>Bundling: OneJAR is composing ${standalone.jar.name}-all.jar</echo>
        <one-jar destfile="${dist.jar.dir}/${standalone.jar.name}-all.jar" manifest="onejar.mf" update="true">
            <main>
                <fileset dir="${build.classes.dir}/" />
            </main>
            <lib>
                <fileset file="${dist.jar.dir}/lib/*.*" />
            </lib>
            <binlib>
                <fileset file="${natives.dir}/*.*" />
            </binlib>
            <fileset file="${basedir}/properties.cfg"></fileset><!-- optional -->
        </one-jar> 
        <echo file="${dist.jar.dir}/README.txt" append="true">
Run the stand-alone distribution from the command line using:
        java -jar -Xmx256m ${standalone.jar.name}-all.jar</echo>
        <echo>Bundling: OneJAR is preparing ${standalone.jar.name}-win.jar</echo>
        <one-jar destfile="${dist.jar.dir}/${standalone.jar.name}-win.jar" manifest="onejar.mf" update="true">
            <main>
                <fileset dir="${build.classes.dir}/" />
            </main>
            <lib>
                <fileset file="${dist.jar.dir}/lib/*.*" />
            </lib>
            <binlib>
                <fileset file="${dist.jar.dir}/natives/win/*.*" />
            </binlib>
        </one-jar>
        <echo>Bundling: OneJAR is preparing ${standalone.jar.name}-linux.jar</echo>
        <one-jar destfile="${dist.jar.dir}/${standalone.jar.name}-linux.jar" manifest="onejar.mf" update="true">
            <main>
                <fileset dir="${build.classes.dir}/" />
            </main>
            <lib>
                <fileset file="${dist.jar.dir}/lib/*.*" />
            </lib>
            <binlib>
                <fileset file="${dist.jar.dir}/natives/linux/*.*" />
            </binlib>
        </one-jar>
        <echo>Bundling: OneJAR is preparing ${standalone.jar.name}-mac.jar</echo>
        <one-jar destfile="${dist.jar.dir}/${standalone.jar.name}-mac.jar" manifest="onejar.mf" update="true">
            <main>
                <fileset dir="${build.classes.dir}/" />
            </main>
            <lib>
                <fileset file="${dist.jar.dir}/lib/*.*" />
            </lib>
            <binlib>
                <fileset file="${dist.jar.dir}/natives/mac/*.*" />
            </binlib>
        </one-jar>
        <echo>Bundling: OneJAR is done.</echo>
    </target>
  1. Change ../SharedLibraries/lib/natives to the path to your native libraries directory.
  2. Change HelloWorld to the name of your application (a name without spaces).


Building the OneJARs

Clean and build your application, and run the dist-all ant target. In NetBeans:

  1. Choose Run > Clean and Build from the menu. (Shortcut: F6, or press the Build toolbar button.)
  2. Open the Files window in NetBeans. (Shortcut PC: ctrl-2, Mac: command-2)
  3. Open your project's file tree so you see the build.xml file.
  4. Right-click build.xml and select Run Target > dist-all (or Run Target > Other Targets > dist-all).
  5. Several stand-alone JARs are added to the dist directory. Each contains the natives needed for one particular operating system.


You can run the OneJar from the commandline using e.g. java -Xmx256m -jar HelloWorld-all.jar.

Zip Distributions

Adding the Ant Zip Task

We want to create JAR Launchers for Windows, Mac OS and Linux, and create zip archives of them.

Add the following Ant target to your build.xml script before the closing </project> tag.

<target name="dist-zip" depends="dist-onejar"
        description="Creating ZIP archives of the standalone OneJAR for windows, mac, linux">
     <echo>Bundling: Creating JAR Launchers</echo>
     <mkdir  dir="${release.dir}" />
     <mkdir  dir="${release.dir}/${standalone.jar.name}-mac" />
     <mkdir  dir="${release.dir}/${standalone.jar.name}-win" />
     <echo  file="${release.dir}/${standalone.jar.name}-win/${standalone.jar.name}.bat">
java -jar -Xmx256m ${standalone.jar.name}-win.jar
     </echo>
     <copy todir="${release.dir}/${standalone.jar.name}-win/"
            file="${dist.jar.dir}/${standalone.jar.name}-win.jar" />
     <mkdir  dir="${release.dir}/${standalone.jar.name}-linux" />
     <echo  file="${release.dir}/${standalone.jar.name}-linux/${standalone.jar.name}.sh">
java -jar -Xmx256m ${standalone.jar.name}-linux.jar
     </echo>
     <copy todir="${release.dir}/${standalone.jar.name}-linux/"
            file="${dist.jar.dir}/${standalone.jar.name}-linux.jar" />
     <echo>Bundling: Creating ZIP Archives</echo>
     <zip destfile="${release.dir}/${standalone.jar.name}-win.zip"  
          basedir="${release.dir}/${standalone.jar.name}-win" update="true" />
     <zip destfile="${release.dir}/${standalone.jar.name}-linux.zip"  
          basedir="${release.dir}/${standalone.jar.name}-linux" update="true" />
     <move todir="${release.dir}/${standalone.jar.name}-mac/"
         file="${dist.jar.dir}/${standalone.jar.name}-mac.jar" >
     </move>
     <zip destfile="${release.dir}/${standalone.jar.name}-mac.zip"
          basedir="${release.dir}/${standalone.jar.name}-mac" update="true" />
     <echo>Bundling: Cleaning up temporary files</echo>
     <delete dir="${release.dir}/${standalone.jar.name}-win" />
     <delete dir="${release.dir}/${standalone.jar.name}-linux" />
     <delete dir="${release.dir}/${standalone.jar.name}-mac" />
     <delete file="${dist.jar.dir}/${standalone.jar.name}-win.jar" />
     <delete file="${dist.jar.dir}/${standalone.jar.name}-linux.jar" />
</target>

<target name="-post-clean">
       <delete dir="${release.dir}" />
</target>

Building the ZIP Files

  1. Clean and Build the project.
  2. Make certain that dist directory was created and contains a JAR file, and a lib directory full of JARs.
  3. To call the dist-zip target manually from the IDE:
    1. Open the Files window in NetBeans. (Shortcut PC: ctrl-2, Mac: command-2)
    2. Open your project's file tree so you see the build.xml file
    3. Right-click build.xml and select Run Target > dist-zip (or Run Target > Other Targets > dist-zip)
  4. The distributable files will be created in the release directory.


Optional: You can assign a shortcut to your custom target in NetBeans.

  1. Open the Files window in NetBeans (pc: ctrl-2, mac: command-2)
  2. Open your project's file tree so you see the build.xml file
  3. Click the triangle next to build.xml to list all targets.
  4. Right-click dist-zip and choose Create Shortcut from the context menu.
  5. Use the wizard to create a keyboard shortcut, a toolbar button, or a menu item for this target.


Distributing the Application

  1. You distribute the files HelloWorld-win.zip, HelloWorld-linux.zip, HelloWorld-mac.zip from the release directory.
  2. Your users download the zip archive for their operating system (win, linux, or mac) and unpack them.
  3. Then they double-click the launcher (for windows and linux) or the .jar file (for mac), respectively, to run the application.
    • (Linux users must give executable permissions to the .sh file first, as usual. chmod u+x HelloWorld.sh )
  4. You can add icons to the launchers.
Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo