5.5 Deploying Self-Contained Applications

«« Previous
Next »»

A self-contained application consists of a single, installable bundle that contains your application and a copy of the JRE needed to run the application. When the application is installed, it behaves the in the same way as any native application. Providing users with a self-contained application avoids the security issues related to running an application in a browser.

You can customize a self-contained application by providing your own icons and progress indicator. File associations can be set up so when a user opens a file that your application can handle, your application is started automatically. Multiple entry points are supported so you can deliver a suite of applications in a single self-contained application bundle.

Self-contained applications can be packaged using the Java Packaging tools. The javavpackager command creates the bundle for self-contained applications from the command line. NetBeans can also be used to created self-contained application bundles. This lesson describes how to use Ant tasks to create the bundles.

Pre-Requisites for Packaging Self-Contained Applications


The Java Development Kit (JDK) is required to compile and package your application. The installable bundle must be created on the platform on which the self-contained application will run. For example, if your application runs on Windows and Linux, you must run the packaging tool on Windows to create a .exe or .msi bundle, and run the packaging tool on Linux to create a .rpm or .deb file.

Third party tools are required to create the installable bundle. The following table identifies the tools for each supported platform.

Platform Format Tool
Alfreds Futterkiste Maria Anders Germany
Windows EXE Inno Setup 5 or later
Windows MSI WiX Toolset 3.8 or later
Linux RPM RPMBuild
Linux DEB Debian packaging tools
OS X DMG
OS X PKG

Converting an Existing Application


Any standalone Java application or Java Web Start application can be packaged as a self-contained application. If you have a Java applet.

Before converting an application, make sure that you have the required pre-requisites installed for your platform.

This section converts the Dynamic Tree Demo from Deploying a Java Web Start Application to a self-contained application. You can download the source files for this demo from Self-Contained Application Examples.

Setting Up the Directories


Identify and organize the files that are needed by your application. A simple application might require only a JAR file. A more complex application might also require additional libraries or resources. Custom resources such as icons or configuration files can also be used by self-contained applications.

The Dynamic Tree Demo requires only the DynamicTreeDemo.jar file, which is in the /dist directory of the project. The HTML and JNLP files that were needed for the Java Web Start version of the application are not needed and are ignored by the bundlers for self-contained applications.

To provide a custom icon for the Dynamic Tree Demo, which represents the application when it is installed on a user's desktop, an icon is provided for each platform supported. These icons are placed in the /src/package/platform directories. The icon is provided in a different format for each supported platform: .ico format for Windows, .png format for Linux, and .icns format for OS X.

The following example shows the directory structure for the Dynamic Tree Demo project before the self-contained bundles are created:

/packager_DynamicTreeDemo     <--- application project
   /dist
      DynamicTreeDemo.jar
      ...
   /src
      /package                <--- custom resources
         /linux
         /macosx
         /windows
      /webstartComponentArch  <--- application source files
      ...

Setting Up the Build File


Set up the Ant tasks for the packaging tasks that are needed. These tasks can be added to the build.xml file for the project, or placed in a separate file that is imported by the build.xml file.

For the Dynamic Tree Demo, the packager.xml file in the root directory of the project contains the Ant tasks for generating the self-contained application bundles. The source for the packager.xml file is shown in the following example:

<project name="DynamicTreePackaging" default="default" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant">
    <echo>${java.home}/../lib/ant-javafx.jar</echo>
    <target name="package" depends="jar">
        <taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
                 uri="javafx:com.sun.javafx.tools.ant"
                 classpath="${java.home}/../lib/ant-javafx.jar;src"/>

        <fx:deploy outdir="${basedir}/build/packager" 
                   outfile="DynamicTreeDemo"
                   nativeBundles="all"
                   verbose="false">

            <fx:application name="Dynamic Tree Demo"
                        mainClass="webstartComponentArch.DynamicTreeApplication"
                        version="1.0"
            />

            <fx:resources>
                <fx:fileset dir="dist" includes="DynamicTreeDemo.jar"/>
            </fx:resources>

            <fx:info title="Dynamic Tree Demo"
                     vendor="My Company"
                     description="A Demo of a Dynamic Swing Tree"
                     category="Demos"
                     copyright="(c) 2014 My Company"
                     license="3 Clause BSD"
            />

            <fx:bundleArgument arg="linux.bundleName" value="dynamic-tree-demo"/>
            <fx:bundleArgument arg="email" value="maintainer@example.com"/>
            <fx:bundleArgument arg="mac.CFBundleName" value="Java Tree Demo"/>
            <fx:bundleArgument arg="win.menuGroup" value="Java Demos"/>

        </fx:deploy>
    </target>
</project>

Use the following information to set up the Ant tasks:
  • Use xmlns:fx="javafx:com.sun.javafx.tools.ant for the namespace.
  • The taskdef task must be executed before the fx:deploy task. The classpath attribute contains the location of the ant-javafx.jar file from the JDK and the directory that contains the custom resources. For the Dynamic Tree Demo, the classpath attribute includes the /src directory, which contains the custom icons.
  • Place the fx:deploy task inside the desired target. Specify the output directory where the native binaries are placed, and specify the native binaries that you want to produce.
  • If all is specified for the native binaries, all possible binaries for the platform on which you execute this task file are generated, including the disk image. Valid values for all platforms are all; image, which generates the file directory on Windows and Linux and the .app file on OSX; and installer, which generates only installable bundles for the platform, not the disk image. Valid values for platform-specific binaries are exe and msi for Windows; deb and rpm for Linux; deb, pkg, and mac.appStore for OS X. You must have the required tools installed to build the binary of your choice.
  • For the Dynamic Tree Demo, the outdir attribute is set to ${basedir}/build/packager. basedir is defined in the project element, in this case it is set to the current directory. The nativeBundles attribute is set to all so all formats for the platform on which the packaging task is run are built.
  • The verbose attribute is optional. Use this attribute to provide diagnostic information.
  • Provide information about the application. Set the name of the application in the name attribute of the fx:application element and the title attribute of the fx:info element. Set the version of the application in the version attribute of the fx:application element. Use the fx:info element to provide a description of the application, the name of the vendor, license information, and other metadata.
  • Information about the JAR file and other resources is set in the fx:resources element.
  • Launch information is set in the mainclass attribute of the fx:application element.
  • For the Dynamic Tree Demo, a simple single launcher is used, webstartComponentArch.DynamicTreeApplication, which is the main class for the application.
  • Other platform-specific customizations are provided in the fx:bundleArgument elements. Arguments that are not recognized by a bundler are ignored, so one build file can contain the packaging information for all platforms.
  • For the Dynamic Tree Demo, the following customizations are applied:
    • The bundle name for Linux is set to dynamic-tree-demo.
    • An email address is provided.
    • The name that appears in the menu bar for OS X is set to Java Tree Demo.
    • The name of the menu group in which the application is stored for Windows is set to Java Demos.

Generating the Bundles


Run the packaging tasks that you created on the platform for which you want to build the bundle for your self-contained application.

For the Dynamic Tree Demo, run the following command from the root folder for the project:

     ant package

When the packaging task completes, the build/packager/bundles directory in the application project contains the native binaries that were produced.

The following example shows the directory structure for the Dynamic Tree Demo project after the self-contained bundles are generated for Windows:

/packager_DynamicTreeDemo     <--- application project
   /build
      /packager
         /bundles
            Dynamic Tree Demo         <---folder image
            Dynamic Tree Demo-1.0.exe <---EXE installer
            Dynamic Tree Demo-1.0.msi <---MSI installer
      ...   
   /dist
      DynamicTreeDemo.jar
      ...
   /src
      /package                <--- custom resources
         /linux
         /macosx
         /windows
      /webstartComponentArch  <--- application source files
      ...
Note that in addition to the self-contained bundles, the packaging tool always generates the JAR, JNLP, annd HTML files for an application. These files provide other options for distributing your application.

Using File Associations


One of the advantages of providing a self-contained application to your users is the ability to set up file associations. Specific types of files can be associated with your application based on MIME type or file extension so that your application is used to open an associated file. For example, if your application edits text files, you can set up a file association that runs your application when a user double-clicks on a file with the extension .txt.

The File Association Demo reads JavaScript and Groovy code. Using both MIME types and file extensions, the application is associated with JavaScript and Groovy files.

You can download the source files for the File Association Demo from Self-Contained Application Examples.

Setting Up File Associations

The Ant tasks for generating the self-contained application bundles are in the build.xml file for the File Association Demo. The <fx:association> Ant element is used to associate file extensions or MIME types with your application. Linux bundlers require the MIME type, Windows bundlers require the file extension, and OS X bundlers require at least one of the properties. It is a good practice to use both properties with a one-to-one mapping between the MIME type and file extension, which enables you to use the same build file on multiple platforms. See <fx:association> for more information about this element.

The following code shows what needs to be included in the fx:deploy element to associate the application with the extensions .js and .groovy and the MIME types text/javascript and text/x-groovy.

<fx:info title="File Association Demo"
         vendor="MySamples"
         description="A Demo of File Associations for Java Packager"
         category="Demos"
         license="3 Clause BSD">
    <fx:association extension="js" mimetype="text/javascript" description="JavaScript Source"/>
    <fx:association extension="groovy" mimetype="text/x-groovy" description="Groovy Source"/>
</fx:info>

If a bundler does not support file associations, the associations are ignored. As of the 8u40 release of the JDK, the Windows EXE and MSI bundlers, Linux DEB and RPM bundlers, and the Mac .app bundler support file associations. The OS X PKG and DMG bundlers support file associations through their use of the Mac .app bundler.

See build.xml for the complete build code.

<!--
 You may freely edit this file. See commented blocks below for 
-->
<!--  some examples of how to customize the build.  -->
<!--
 (If you delete it and reopen the project it will be recreated.) 
-->
<!--
 By default, only the Clean and Build commands use this build script. 
-->
<!--
 Commands such as Run, Debug, and Test only use this build script if 
-->
<!--
 the Compile on Save feature is turned off for the project. 
-->
<!--
 You can turn off the Compile on Save (or Deploy on Save) setting 
-->
<!--  in the project's Project Properties dialog box. -->
<project name="packager_FileAssociations" default="default" basedir=".">
<description>
Builds, tests, and runs the project packager_FileAssociations.
</description>
<import file="nbproject/build-impl.xml"/>
<target name="-pre-init">
<!--  download and copy groovy library  -->
<copy toFile="lib/groovy-all-2.3.8.jar">
<resources>
<url url="http://central.maven.org/maven2/org/codehaus/groovy/groovy-all/2.3.8/groovy-all-2.3.8.jar"/>
</resources>
</copy>
<condition property="excludes" value="**/*Mac.java">
<not>
<os family="mac"/>
</not>
</condition>
<condition property="main.class" value="sample.fa.ScriptRunnerApplication" else="sample.fa.ScriptRunnerApplicationMac">
<not>
<os family="mac"/>
</not>
</condition>
<property name="javac.compilerargs" value="-XDignore.symbol.file=true"/>
</target>
<target name="-post-jar">
<copy todir="dist">
<fileset dir="src">
<exclude name="**/*.java"/>
</fileset>
<fileset dir="." includes="lib/*"/>
</copy>
</target>
<target xmlns:fx="javafx:com.sun.javafx.tools.ant" name="package" depends="jar">
<taskdef resource="com/sun/javafx/tools/ant/antlib.xml" uri="javafx:com.sun.javafx.tools.ant" classpath="${java.home}/../lib/ant-javafx.jar;src"/>
<fx:deploy outdir="${basedir}/build/packager" outfile="FileAssociationDemo" nativeBundles="all" verbose="false">
<fx:application id="fileassociationdemo" name="File Association Demo" mainClass="${main.class}" version="1.0">
<fx:argument>sample.js</fx:argument>
</fx:application>
<fx:resources>
<fx:fileset dir="dist" includes="**/*"/>
</fx:resources>
<fx:info title="File Association Demo" vendor="My Company" description="A Demo of a File Associations for JavaPackager" category="Demos" copyright="(c) 2014 My Company" license="3 Clause BSD">
<fx:association extension="js" mimetype="text/javascript" description="JavaScript Source"/>
<fx:association extension="groovy" mimetype="text/x-groovy" description="Groovy Source"/>
</fx:info>
<fx:bundleArgument arg="classpath" value="FileAssociationsDemo.jar lib/groovy-all-2.3.8.jar"/>
<fx:bundleArgument arg="win.exe.systemWide" value="true"/>
<fx:bundleArgument arg="linux.bundleName" value="file-association-demo"/>
<fx:bundleArgument arg="email" value="maintainer@example.com"/>
<fx:bundleArgument arg="mac.CFBundleName" value="File Assoc Demo"/>
<fx:bundleArgument arg="win.menuGroup" value="Java Demos"/>
</fx:deploy>
</target>
</project>

To generate the installable bundles for the File Association Demo, see the "Generating the Bundles" section in Converting an Existing Application.

Launching from Associated Files

File associations are set up by the installer when the self-contained application bundle is installed on a user's system. After the application is installed, opening a file that is associated with your application causes your application to be started. The actions taken to launch your application depend on the platform on which it is running.

Launching on Linux and Windows

On Linux and Windows, when an application is launched based on a file association, the files being opened are passed as arguments to the main class, which overrides the default argument for the class. For the File Associations Demo, the arguments are passed to the loadscript method after an instance of the application is started. A different instance of the application is started for each file opened.

See ScriptRunnerApplication.java for the Linux and Windows version of the code.

package sample.fa;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.util.Optional;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.swing.Box;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

/**
 *
 * @author dferrin
 */
public class ScriptRunnerApplication {
    
    private DefaultComboBoxModel<ScriptEngine> languagesModel;
    private JTextArea scriptContents;
    private JTextArea scriptResults;

    void createGUI() {
        Box buttonBox = Box.createHorizontalBox();
        
        JButton loadButton = new JButton("Load");
        loadButton.addActionListener(this::loadScript);
        buttonBox.add(loadButton);

        JButton saveButton = new JButton("Save");
        saveButton.addActionListener(this::saveScript);
        buttonBox.add(saveButton);
        
        JButton executeButton = new JButton("Execute");
        executeButton.addActionListener(this::executeScript);
        buttonBox.add(executeButton);
        
        languagesModel = new DefaultComboBoxModel();

        ScriptEngineManager sem = new ScriptEngineManager();
        for (ScriptEngineFactory sef : sem.getEngineFactories()) {
            languagesModel.addElement(sef.getScriptEngine());
        }       
        
        JComboBox<ScriptEngine> languagesCombo = new JComboBox<>(languagesModel);
        JLabel languageLabel = new JLabel();
        languagesCombo.setRenderer((JList<? extends ScriptEngine> list, 
                ScriptEngine se, int index, boolean isSelected, 
                boolean cellHasFocus)
        -> {
            ScriptEngineFactory sef = se.getFactory();
            languageLabel.setText(
                sef.getEngineName() + " - " + 
                sef.getLanguageName() + 
                " (*." + String.join(", *.", sef.getExtensions()) + ")"
            );
            return languageLabel;
        });
        buttonBox.add(Box.createHorizontalGlue());
        buttonBox.add(languagesCombo);  
        
        scriptContents = new JTextArea();
        scriptContents.setRows(8);
        scriptContents.setColumns(40);
        
        scriptResults = new JTextArea();
        scriptResults.setEditable(false);
        scriptResults.setRows(8);
        scriptResults.setColumns(40);

        JSplitPane jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scriptContents, scriptResults);
        
        JFrame frame = new JFrame("Script Runner");
        frame.add(buttonBox, BorderLayout.NORTH);
        frame.add(jsp, BorderLayout.CENTER);
        
        frame.pack();
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        frame.setVisible(true);
    }
    
    private void loadScript(ActionEvent ae) {
        JFileChooser jfc = new JFileChooser();
        jfc.showOpenDialog(scriptContents);
        if (jfc.getSelectedFile() != null) {
            loadScript(jfc.getSelectedFile());
        }
    }
    
    void loadScript(File f) {
        try {        
            scriptContents.setText(String.join("\n", Files.readAllLines(f.toPath())));

            String name = f.getName();
            int lastDot = name.lastIndexOf('.');
            if (lastDot >= 0) {
                String extension = name.substring(lastDot + 1);
                for (int i = 0; i < languagesModel.getSize(); i++) {
                    ScriptEngine se = languagesModel.getElementAt(i);
                    if (se.getFactory().getExtensions().indexOf(extension) >= 0) {
                        languagesModel.setSelectedItem(se);
                        break;
                    }
                }
            }
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(scriptContents, ex.getMessage(), "Error Loading Script", JOptionPane.ERROR_MESSAGE);
        }
    }

    private void saveScript(ActionEvent ae) {
        JFileChooser jfc = new JFileChooser();
        jfc.showOpenDialog(scriptContents);
        File f = jfc.getSelectedFile();
        if (f != null) {
            try {
                Files.write(f.toPath(), scriptContents.getText().getBytes());
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(scriptContents, ex.getMessage(), "Error Saving Script", JOptionPane.ERROR_MESSAGE);
            }
        }
        
    }
    
    private void executeScript(ActionEvent ae) {
        ScriptEngine se = (ScriptEngine) languagesModel.getSelectedItem();
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
            Writer w = new OutputStreamWriter(baos);) 
        {
            se.getContext().setWriter(w);
            
            Optional<Object> result = Optional.ofNullable(se.eval(scriptContents.getText()));
            
            scriptResults.setText(baos.toString() + "\n>>>>>>\n" + 
                    result.orElse("-null-").toString());
        } catch (Exception e) {
            e.printStackTrace(System.out);
            scriptResults.setText(e.getClass().getName() + " - " + e.getMessage());
        }
    }

    public static void main(String [] args) {
        SwingUtilities.invokeLater(() -> {
            ScriptRunnerApplication app = new ScriptRunnerApplication();
            app.createGUI();
            if (args.length > 0) {
                File f = new File(args[0]);
                if (f.isFile()) {
                    app.loadScript(f);
                }
            }
        });
    }
}

Launching on OS X

On OS X, only a single instance of an application is run. When an associated file is opened, an event is sent to the application. The application must have an event listener registered to handle the event.

The File Association Demo for OS X has a subclass with a different main method than the version for Linux and Windows. This main method handles the default argument in the same way as the main method for the Linux and Windows version, and then registers a listener with OS X for FileOpenHandler. The event method for this listener is called when an associated file is opened, and the file name is extracted from the getFiles method of the OpenFilesEvent object.

See ScriptRunnerApplicationMac.java for the OS X version of the code.

package sample.fa;

import com.apple.eawt.AppEvent;
import com.apple.eawt.Application;
import java.io.File;
import java.util.List;
import javax.swing.SwingUtilities;

/**
 *
 * @author dferrin
 */
public class ScriptRunnerApplicationMac extends ScriptRunnerApplication {
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            ScriptRunnerApplication app = new ScriptRunnerApplication();
            app.createGUI();
            if (args.length > 0) {
                File f = new File(args[0]);
                if (f.isFile()) {
                    app.loadScript(f);
                }
            }
            
            Application.getApplication().setOpenFileHandler((AppEvent.OpenFilesEvent ofe) -> {
                List<File> files = ofe.getFiles();
                if (files != null && files.size() > 0) {
                    app.loadScript(files.get(0));
                }
            });
            
        });
    }
}

Building the OS X version of the File Association Demo requires access to the OS X-specific classes that come with the Oracle JDK. Most com.apple.eawt classes are not included in the symbols file that the javac compiler uses. To tell the compiler to ignore the symbols file, the -XDignore.symbol.file=true argument is passed to the javac compiler in the -pre-init Ant task in the build file.

More About the File Association Demo


The project for the File Association Demo contains the Java source files for the application in the /src/sample/fa directory. Custom icons are provided in the /src/package/platform directory. Sample files to be packaged with the application are in the /src directory.

To handle Groovy code, the File Association Demo requires the Groovy library. The build process downloads the Groovy library to the /lib directory. See Adding an External Library for information.

After the JAR file is generated, the build process copies the /src and /lib directories to the /dist directory. The /dist directory then contains all of the files for your application.

The File Association Demo takes a file name as an argument. If the application is started by opening an associated file, the name of the associated file is passed in. If the application is started directly, the sample file sample.js, which is bundled with the application, is passed in. 

Admin privileges are required to set up file associations. By default the EXE installer for Windows does not request admin privileges. To force a request for admin privileges for the File Association Demo, the bundle argument win.exe.systemWide is set to true. This setting indicates that a system-wide installation is performed, which requires admin privileges.

The File Association Demo runs on Linux, OS X, and Windows. The demo is set up to use a single build file that contains the information for all platforms.

Adding an External Library


Self-contained applications include everything that an application needs to run. If your application requires an external library, that library can be added to the bundle for the application. Adding the library can be done in different ways.

The File Association Demo described in Using File Associations downloads the Groovy library as part of the build process. The library is placed in the /lib directory in the project for the application. This directory is then copied to the /dist directory from which the self-contained application bundle is generated.

The following code in the -pre-init task in the build.xml file shows how the library is downloaded:

<!-- download and copy groovy library -->
<copy toFile="lib/groovy-all-2.3.8.jar">
    <resources>
      <url url="http://central.maven.org/maven2/org/codehaus/groovy/groovy-all/2.3.8/groovy-all-2.3.8.jar"/>
    </resources>
</copy>

Providing a Default Argument


Arguments are passed to Java applications when an application is started. Self-contained applications can be set up with a default argument that is used when no argument is specified. The <fx:argument> element is used to define the argument. More than one argument can be passed by adding an <fx:argument> element for each argument. See <fx:argument> for information about this element.

The File Association Demo described in Using File Associations is set up use the name of one of the sample files that is bundled with the application as the default argument.

The following code in the <fx:deploy> task in the build.xml file shows how the default argument is defined:

<fx:application id="fileassociationdemo"
                name="File Association Demo"
                mainClass="${main.class}"
                version="1.0">
    <fx:argument>sample.js</fx:argument>
</fx:application>

Using a Common Build File for All Platforms


To generate a self-contained application bundle for each platform on which your application runs, you must run the packaging tool on each platform. You have the option of using platform-specific build files or setting up one build file that can be run on all platforms. Platform-specific files can be simpler to set up, but then you must maintain multiple files.

The File Association Demo described in Using File Associations uses a single build file that works on all platforms.

The following elements of the build file support its use for all platforms:
  • The main class for the application is ScriptRunnerApplication.java for Linux and Windows and ScriptRunnerApplicationMac.java for OS X. The following code in the -pre-init task is used to determine which class to use:
<condition property="main.class" 
           value="sample.fa.ScriptRunnerApplication"
           else="sample.fa.ScriptRunnerApplicationMac">
    <not><os family="mac"/></not>
</condition>
  • The following code in the -pre-init task is used to prevent the main class for OS X from being compiled when running on Linux or Windows:
<condition property="excludes" value="**/*Mac.java">
    <not><os family="mac"/></not>
</condition>
  • <fx:bundleArgument> elements are used to pass arguments to the different bundlers available. Arguments that are not used by a bundler are ignored, so the build file can contain the arguments needed by all platforms. The following code defines arguments for Linux, OS X, and Windows:
<fx:bundleArgument arg="classpath" value="FileAssociationsDemo.jar lib/groovy-all-2.3.8.jar"/>

<fx:bundleArgument arg="win.exe.systemWide" value="true"/>

<fx:bundleArgument arg="linux.bundleName" value="file-association-demo"/>
<fx:bundleArgument arg="email" value="maintainer@example.com"/>
<fx:bundleArgument arg="mac.CFBundleName" value="File Assoc Demo"/>
<fx:bundleArgument arg="win.menuGroup" value="Java Demos"/>

Using Multiple Entry Points


Self-contained applications are useful when you have a set of related applications that you want users to deploy. A self-contained application provides a single, installable bundle that installs all of the applications and the JRE needed to run them.

The Multiple Launchers Demo includes the Dynamic Tree Demo described in Converting an Existing Application and the File Association Demo described in Using File Associations. The /src directory of the project contains the source file for both applications.

The primary entry point for a self-contained application is identified by the mainClass attribute of the <fx:application> element. In the Multiple Launchers Demo, the primary entry point is the File Association Demo. The main class is sample.fa.ScriptRunnerAppliation for Linux and Windows, or sample.fa.ScriptRunnerApplicationMac for OS X.

Each secondary entry point is identified by an instance of the <fx:secondaryLauncher> element. 

In the Multiple Launchers Demo, the secondary entry point is the Dynamic Tree Demo. The following code in the build.xml file shows how the second entry point is defined:

<fx:secondaryLauncher name="Dynamic Tree Demo"
    mainClass="webstartComponentArch.DynamicTreeApplication"
    version="1.0"
    title="Dynamic Tree Demo"
    vendor="My Company"
    description="A Demo of Multiple Launchers for JavaPackager"
    copyright="(c) 2014 My Company"
     menu="true"
     shortcut="false"
     >
</fx:secondaryLauncher>

To generate the installable bundles for the Multiple Launchers Demo, see the "Generating the Bundles" section in Converting an Existing Application.

When your self-contained application is installed, the File Association Demo is installed with the Multiple Launchers entry point and the Dynamic Tree Demo is installed with its own entry point. For example, on Windows, the Java Demos folder in the Start menu contains two entries: Dynamic Tree Demo and Multiple Launchers Demo. Note that file associations are set up for the Multiple Launchers entry point, so opening a JavaScript or Groovy file starts Multiple Launchers.

Answers to Questions and Exercises: Self-Contained Applications

Questions

1. Question: Which of the following items is not an advantage of self-contained applications?

A. Users install the application with an installer that is familiar to them.
B. The application runs as a native application.
C. The application requires less space on a user's machine.
D. You control the version of the JRE that is used by the application.
E. The application does not require a browser to run.
Answer: C. The application requires more space because the JRE is bundled with the application.

2. Question: True or False: MIME type must always be used to set up a file association.

Answer: False. Depending on the platform and the bundler used, a MIME type or file extension can be used. For Linux, the MIME type is required. For Windows, the file extension is required. For OS X, either the MIME type or the file extension is required. It is a good practice to provide both the MIME type and file extension when setting up file associations, regardless of the platform.

3. Question: What elements are used to identify the entry points for self-contained applications in the <fx:deploy> Ant task?

Answer: The mainClass attribute of the <fx:application> element is used to identify the main entry point. If the self-contained application has multiple entry points, the <fx:secondaryLauncher> element is used for each secondary entry point.

Exercises

1. Exercise: Write the <fx:deploy> Ant task to generate a Windows MSI bundle for a simple application named My Sample App. The JAR file for the application is in the dist directory, the main class is samples.MyApp, and output files are to be written to the current directory.
Answer:

<fx:deploy outdir="."
           outfile="MySampleApp"
           nativeBundles="msi>

    <fx:application name="My Sample Application"
                    mainClass="samples.MyApp"/>

    <fx:resources>
        <fx:fileset dir="dist" includes="*.jar"/>
    </fx:resources>

    <fx:info title="My Sample Application"
             description="A simple sample app"/>
</fx:deploy>

2. Exercise: Enhance the code in the previous exercise to create bundles for all Windows installers and define a file association for text files.

Answer:

<fx:deploy outdir="."
           outfile="MySampleApp"
           nativeBundles="installer">

    <fx:application name="My Sample Application"
                    mainClass="samples.MyApp"/>

    <fx:resources>
        <fx:fileset dir="dist" includes="*.jar"/>
    </fx:resources>

    <fx:info title="My Sample Application"
             description="A simple sample app">
        <fx:association extension="txt" 
                        description="Text files">
         </fx:association>
     </fx:info>
</fx:deploy>
When the nativeBundles attribute is set to installer, the packager attempts to build bundles for all supported installers for that platform. The disk image is not created. If the tools needed to build a particular bundle are not available, that bundle type is skipped.

Windows requires only the extension attribute when defining file associations.

«« Previous
Next »»