This chapter shows how to use Ant to package JavaFX application.
JavaFX Ant tasks and the JavaFX Packager tool are currently the only supported ways to package JavaFX applications. This includes supported versions of the NetBeans IDE, which build JavaFX applications with JavaFX Ant tasks.
This page contains the following topics:
See also the following two Ant Task Reference sections:
The ant-javafx.jar file is required to use these tasks. It is located in the following locations:
In JDK 7 Update 6 or later, it is located in jdk_home/lib
In a standalone JavaFX installation, it is located in javafx-sdk-home/lib
There are two categories of Ant elements for JavaFX. Each of the following elements is described in JavaFX Ant Task Reference.
JavaFX Ant Tasks
These elements accomplish the following tasks:
Creating double-clickable JAR files
Creating an HTML page and deployment descriptor for Web Start applications or applications embedded in a web page
Digitally signing an application, when necessary
Converting CSS files to binary format
Assembling self-contained application packages
See JavaFX Ant Task Reference. For general information about packaging for JavaFX applications, see Chapter 5, "Packaging Basics" and Chapter 7, "Self-Contained Application Packaging."
Ant Helper Parameters
These elements are used by the JavaFX tasks. They are listed and described in JavaFX Ant Helper Parameter Reference.
To use the JavaFX Ant tasks in the your Ant script, you must load their definitions. An example is shown in the build.xml file in Example 10-1:
Example 10-1 Load JavaFX Ant Task Definitions
<project name="JavaFXSample" default="default" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant"> <target name="default"> <taskdef resource="com/sun/javafx/tools/ant/antlib.xml" uri="javafx:com.sun.javafx.tools.ant" classpath=".:path/to/sdk/lib/ant-javafx.jar"/> </target> </project>
Notes about Example 10-1:
Ensure that you declare the fx: namespace, shown in bold in Example 10-1, because short names for some of JavaFX tasks are the same as those used for some system tasks.
The current directory (".") is added to the classpath to simplify customization using drop-in resources. See Section 7.3.3, "Customizing the Package Using Drop-In Resources."
Once JavaFX Ant task definitions are loaded, the javafx.ant.version
property can be used to check the version of Ant tasks APIs. Use the following list for version numbers:
Version 1.0: shipped in the JavaFX 2.0 SDK
Version 1.1: shipped in the JavaFX 2.1 SDK
Version 1.2: shipped in the JavaFX 2.2 SDK and JDK 7 Update 6
This section covers the following topics:
Section 10.4.2, "Deploying the JavaFX Hello World Example as a Self-Contained Application"
Section 10.4.3, "Deploying a JavaFX Application with External JAR Files"
Section 10.4.4, "Overriding JVM Options for Self-Contained Applications"
Follow these steps to deploy the JavaFX Hello World example as a JAR file with an Ant script:
Create a directory to contain the example application. These steps use the directory C:\example
.
Save Example 10-2 as C:\example\src\HelloWorld.java
:
Example 10-2 HelloWorld.java
import javafx.application.Application; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class HelloWorld extends Application { @Override public void start(Stage primaryStage) { Button btn = new Button(); btn.setText("Say 'Hello World'"); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { System.out.println("Hello World!"); } }); StackPane root = new StackPane(); root.getChildren().add(btn); Scene scene = new Scene(root, 300, 250); primaryStage.setTitle("Hello World!"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
Save Example 10-3 as C:\example\build.xml
:
Example 10-3 Ant Script to Deploy JavaFX Hello World Example
<?xml version="1.0" encoding="UTF-8" ?> <project name="JavaFX Hello World Example" default="default" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant"> <property name="JAVA_HOME" value="C:\\Java\\jdk1.8.0"/> <property name="build.src.dir" value="src"/> <property name="build.classes.dir" value="classes"/> <property name="build.dist.dir" value="dist"/> <target name="default" depends="clean,compile"> <taskdef resource="com/sun/javafx/tools/ant/antlib.xml" uri="javafx:com.sun.javafx.tools.ant" classpath="${JAVA_HOME}/lib/ant-javafx.jar"/> <fx:application id="HelloWorldID" name="JavaFXHelloWorldApp" mainClass="HelloWorld"/> <fx:resources id="appRes"> <fx:fileset dir="${build.dist.dir}" includes="HelloWorld.jar"/> </fx:resources> <fx:jar destfile="${build.dist.dir}/HelloWorld.jar"> <fx:application refid="HelloWorldID"/> <fx:resources refid="appRes"/> <fileset dir="${build.classes.dir}"/> </fx:jar> <fx:deploy width="300" height="250" outdir="." embedJNLP="true" outfile="helloworld"> <fx:application refId="HelloWorldID"/> <fx:resources refid="appRes"/> <fx:info title="JavaFX Hello World Application" vendor="Oracle Corporation"/> </fx:deploy> </target> <target name="clean"> <mkdir dir="${build.classes.dir}"/> <mkdir dir="${build.dist.dir}"/> <delete> <fileset dir="${build.classes.dir}" includes="**/*"/> <fileset dir="${build.dist.dir}" includes="**/*"/> </delete> </target> <target name="compile" depends="clean"> <javac includeantruntime="false" srcdir="${build.src.dir}" destdir="${build.classes.dir}" fork="yes" executable="${JAVA_HOME}/bin/javac" source="1.8" debug="on"> </javac> </target> </project>
In the file build.xml
, specify the location of the JDK installed in your computer by changing the value of the JAVA_HOME
property. Change the highlighted text to the full path of your JDK:
<property name="JAVA_HOME" value="C:\\Java\\jdk1.8.0"/>
At a command-line prompt, change directory to C:\example
. Run the following command to compile, build, and deploy the JavaFX HelloWorld example:
ant
To run the example, at a command-line prompt, change directory to C:\example\dist
and run the following command:
java -jar HelloWorld.jar
To deploy the JavaFX Hello World example as a self-contained application, add the attribute nativeBundles="all"
to the element <fx:deploy>
to the build.xml
script:
<fx:deploy width="300" height="250" outdir="." embedJNLP="true" outfile="helloworld" nativeBundles="all">
Compile, build, and deploy the JavaFX Hello World example as described in the previous section. The Ant script creates all applicable self-contained application packages and stores them in the directory C:\example\bundles\JavaFXHelloWorldApp
. (The name JavaFXHelloWorldApp
is the value of the name
attribute of the <fx:application>
element.) If you are deploying the JavaFX Hello World example with Windows, for example, the Ant script creates an application named C:\example\bundles\JavaFXHelloWorldApp\JavaFXHelloWorldApp.exe
that you can run by double-clicking it in a file browser.
You can customize how your Ant script creates self-contained applications. See Chapter 7, "Self-Contained Application Packaging."
The Ensemble8 sample application requires Apache Lucene. The following steps show you how to deploy the Ensemble8 sample application as a self-contained application and include the Apache Lucene JAR files in it.
Download the JDK Demos and Samples for your operating system from the following page:
http://www.oracle.com/technetwork/java/javase/downloads/index.html
Unzip the file that you downloaded. The Ensemble8 sample is contained in the directory demo/javafx_samples/src/Ensemble8
.
Replace the contents of the file demo/javafx_samples/src/Ensemble8/build.xml
with Example 10-4:
Example 10-4 Ant Script to Deploy Ensemble8 Sample Application
<?xml version="1.0" encoding="UTF-8" ?> <project name="Ensemble8 JavaFX Demo Application" default="default" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant"> <property name="JAVA_HOME" value="C:\\Java\\jdk1.8.0"/> <path id="CLASSPATH"> <pathelement location="lib/lucene-core-3.2.0.jar"/> <pathelement location="lib/lucene-grouping-3.2.0.jar"/> <pathelement path="classes"/> </path> <property name="build.src.dir" value="src"/> <property name="build.classes.dir" value="classes"/> <property name="build.dist.dir" value="dist"/> <target name="default" depends="clean,compile"> <taskdef resource="com/sun/javafx/tools/ant/antlib.xml" uri="javafx:com.sun.javafx.tools.ant" classpath="${JAVA_HOME}/lib/ant-javafx.jar"/> <fx:application id="ensemble8" name="Ensemble8" mainClass="ensemble.EnsembleApp"/> <fx:resources id="appRes"> <fx:fileset dir="${build.dist.dir}" includes="ensemble8.jar"/> <fx:fileset dir="lib"/> <fx:fileset dir="${build.classes.dir}"/> </fx:resources> <fx:jar destfile="${build.dist.dir}/ensemble8.jar"> <fx:application refid="ensemble8"/> <fx:resources refid="appRes"/> </fx:jar> <fx:deploy outdir="." embedJNLP="true" outfile="ensemble8" nativeBundles="all"> <fx:application refId="ensemble8"/> <fx:resources refid="appRes"/> <fx:info title="Ensemble8 JavaFX Demo Application" vendor="Oracle Corporation"/> </fx:deploy> </target> <target name="clean"> <mkdir dir="${build.classes.dir}"/> <mkdir dir="${build.dist.dir}"/> <delete> <fileset dir="${build.classes.dir}" includes="**/*"/> <fileset dir="${build.dist.dir}" includes="**/*"/> </delete> </target> <target name="compile" depends="clean"> <javac includeantruntime="false" srcdir="${build.src.dir}" destdir="${build.classes.dir}" fork="yes" executable="${JAVA_HOME}/bin/javac" source="1.8" debug="on" classpathref="CLASSPATH"> </javac> <!-- Copy resources to build.classes.dir --> <copy todir="${build.classes.dir}"> <fileset dir="src/app/resources"/> <fileset dir="src/generated/resources"/> <fileset dir="src/samples/resources"/> </copy> </target> </project>
Note: After the Ant script compiles the sample application, it copies resources contained in the <copy todir="${build.classes.dir}"> <fileset dir="src/app/resources"/> <fileset dir="src/generated/resources"/> <fileset dir="src/samples/resources"/> </copy> The following lines from the Ant script include the Apache Lucerne JAR files (which are contained in the <fx:resources id="appRes"> <fx:fileset dir="${build.dist.dir}" includes="ensemble8.jar"/> <fx:fileset dir="lib"/> <fx:fileset dir="${build.classes.dir}"/> </fx:resources> <fx:jar destfile="${build.dist.dir}/ensemble8.jar"> <fx:application refid="ensemble8"/> <fx:resources refid="appRes"/> </fx:jar> |
In the file build.xml
, specify the location of the JDK installed in your computer by changing the value of the JAVA_HOME
property. Change the highlighted text to the full path of your JDK:
<property name="JAVA_HOME" value="C:\\Java\\jdk1.8.0"/>
At a command-line prompt, change directory to demo/javafx_samples/src/Ensemble8
. Run the following command to compile, build, and deploy the Ensemble8 sample application:
ant
To run the Ensemble8 sample application, run one of the applications contained in demo/javafx_samples/src/Ensemble8/bundles/Ensemble8
.
You can override JVM options in your self-contained applications by specifying them in a preferences node, then setting the name of this node in the app.preferences.id
system property. The following example overrides the -Xms
and -Xmx
JVM options specified in <fx:jvmuserarg> elements. To verify that these options have been overridden, the application displays the initial and maximum sizes of the memory allocation pool (based on the values of the -Xms
and -Xmx
options). See Specifying User JVM Arguments for more information about specifying JVM options.
Create a directory to contain the example application. These steps use the directory C:\memexample
.
Save Example 10-5 as C:\memexample\src\MemoryExamplePreferences.java
.
The following statement retrieves the java.util.prefs.Preferences
node that stores the values of the options -Xms
and -Xmx
in your computer:
prefs = Preferences.userRoot().node(System.getProperty("app.preferences.id")).node("JVMUserOptions");
The name of this node is app.preferences.id/JVMUserOptions
. The value of the app.preferences.id
property is the value of the id
attribute of the <fx:application> element. In this example, the value of app.preferences.id
is MemoryTestAppID
. When you run a self-contained application, the launcher program sets the system property automatically. However, if you run the class directly, you must specify the value of app.preferences.id
as a system property. For example, if you run this class from the command line, you would specify the value of app.preferences.id
as follows:
java -cp classes -Dapp.preferences.id=MemoryTestAppID
Note that in this example, the Ant build script runs the MemoryTestPreferences
class for you and sets the value of the app.preferences.id
property.
For example, if you are using Microsoft Windows, then this class creates a preferences node named Computer\HKEY_CURRENT_USER\Software\JavaSoft\Prefs\MemoryTestAppID\JVMUserOptions
in the Windows registry.
Example 10-5 MemoryExamplePreferences.java
import java.util.prefs.Preferences; public class MemoryTestPreferences { private Preferences prefs; public void setPreferences() { // This will define a node in which the preferences can be stored prefs = Preferences.userRoot().node(System.getProperty("app.preferences.id")).node("JVMUserOptions"); // now set the values prefs.put("-Xmx", "2048m"); prefs.put("-Xms", "2048m"); } public static void main(String[] args) { MemoryTestPreferences myPrefs = new MemoryTestPreferences(); myPrefs.setPreferences(); } }
Save Example 10-6 as C:\memexample\src\MemoryExample.java
:
Example 10-6 MemoryExample.java
import javafx.application.Application; import javafx.collections.FXCollections; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.TextField; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.stage.Stage; import java.lang.management.ManagementFactory; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; public class MemoryTest extends Application { @Override public void start(Stage primaryStage) { String startMemory = Long.toString(Runtime.getRuntime().totalMemory()); String maxMemory = Long.toString(Runtime.getRuntime().maxMemory()); System.out.println("Start memory: " + startMemory); System.out.println("Max memory: " + maxMemory); final Label startMemoryLabel = new Label("Start memory: "); TextField startMemoryTextField = new TextField(startMemory); startMemoryTextField.setPromptText(startMemory); Label maxMemoryLabel = new Label("Max memory: "); final TextField maxMemoryTextField = new TextField(maxMemory); maxMemoryTextField.setPromptText(maxMemory); Label jvmArgumentsLabel = new Label("JVM Arguments"); ListView<String> jvmArguments = new ListView<>(FXCollections.observableArrayList(ManagementFactory.getRuntimeMXBean().getInputArguments())); jvmArguments.setPrefSize(450, 150); Button btn = new Button(); btn.setText("Update Preferences"); btn.setOnAction(event -> { Preferences prefs = Preferences.userRoot().node(System.getProperty("app.preferences.id")).node("JVMUserOptions"); String start = startMemoryTextField.getText(); if (start == null || start.isEmpty()) { prefs.remove("-Xms"); } else { prefs.put("-Xms", start); } String max = maxMemoryTextField.getText(); if (max == null || max.isEmpty()) { prefs.remove("-Xmx"); } else { prefs.put("-Xmx", max); } try { prefs.flush(); } catch (BackingStoreException e) { e.printStackTrace(); } }); GridPane grid = new GridPane(); grid.setAlignment(Pos.CENTER); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(25, 25, 25, 25)); grid.getColumnConstraints().setAll( new ColumnConstraints(Region.USE_PREF_SIZE, Region.USE_COMPUTED_SIZE, Region.USE_PREF_SIZE, Priority.NEVER, HPos.RIGHT, false), new ColumnConstraints(Region.USE_PREF_SIZE, Region.USE_COMPUTED_SIZE, Integer.MAX_VALUE, Priority.ALWAYS, HPos.LEFT, true) ); grid.addRow(0, startMemoryLabel, startMemoryTextField); grid.addRow(1, maxMemoryLabel, maxMemoryTextField); grid.addRow(2, jvmArgumentsLabel, jvmArguments); grid.add(btn, 1, 3); grid.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); Scene scene = new Scene(grid); primaryStage.setTitle("Memory test"); primaryStage.sizeToScene(); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
Save Example 10-7 as C:\memexample\build.xml
.
This Ant script compiles and runs the MemoryTestPreferences class, and sets the value of the app.preferences.id
property:
<java fork="true" jvm="${env.JAVA_HOME}\bin\java" classname="MemoryTestPreferences" classpath="${build.classes.dir}"> <sysproperty key="app.preferences.id" value="MemoryTestAppID"/> </java>
Example 10-7 Ant Script for Memory Test Example
<?xml version="1.0" encoding="UTF-8" ?> <project name="JavaFX Hello World Example" default="default" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant"> <property environment="env"/> <property name="env.JAVA_HOME" value="C:\\Java\\jdk1.8.0"/> <property name="build.src.dir" value="src"/> <property name="build.classes.dir" value="classes"/> <property name="build.dist.dir" value="dist"/> <target name="default" depends="clean,compile"> <taskdef resource="com/sun/javafx/tools/ant/antlib.xml" uri="javafx:com.sun.javafx.tools.ant" classpath="${env.JAVA_HOME}/lib/ant-javafx.jar"/> <fx:application id="MemoryTestAppID" name="JavaFXMemoryTestApp" mainClass="MemoryTest"/> <fx:resources id="appRes"> <fx:fileset dir="${build.dist.dir}" includes="MemoryTest.jar"/> </fx:resources> <fx:jar destfile="${build.dist.dir}/MemoryTest.jar"> <fx:application refid="MemoryTestAppID"/> <fx:resources refid="appRes"/> <fileset dir="${build.classes.dir}"/> </fx:jar> <fx:deploy width="300" height="250" outdir="." embedJNLP="true" outfile="memorytest" nativeBundles="image"> <fx:platform> <fx:jvmuserarg name="-Xms" value="31m"/> <fx:jvmuserarg name="-Xmx" value="64m"/> </fx:platform> <fx:application refId="MemoryTestAppID"/> <fx:resources refid="appRes"/> <fx:info title="JavaFX Hello World Application" vendor="Oracle Corporation"/> </fx:deploy> </target> <target name="clean"> <mkdir dir="${build.classes.dir}"/> <mkdir dir="${build.dist.dir}"/> <delete> <fileset dir="${build.classes.dir}" includes="**/*"/> <fileset dir="${build.dist.dir}" includes="**/*"/> </delete> </target> <target name="compile" depends="clean"> <javac includeantruntime="false" srcdir="${build.src.dir}" destdir="${build.classes.dir}" fork="yes" executable="${env.JAVA_HOME}/bin/javac" source="1.8" debug="on"> </javac> <!-- Set preferences --> <java fork="true" jvm="${env.JAVA_HOME}\bin\java" classname="MemoryTestPreferences" classpath="${build.classes.dir}"> <sysproperty key="app.preferences.id" value="MemoryTestAppID"/> </java> </target> <target name="jar" depends="compile"> <jar destfile="dist/MemoryTest.jar" basedir="classes"/> </target> </project>
In the file build.xml
, specify the location of the JDK installed in your computer by changing the value of the JAVA_HOME
property. Change the highlighted text to the full path of your JDK:
<property name="JAVA_HOME" value="C:\\Java\\jdk1.8.0"/>
At a command-line prompt, change directory to C:\memexample
. Run the following command to compile, build, and deploy this example:
ant
To run the Memory Test application, run one of the applications contained in C:\memexample\bundles\bundles/JavaFXMemoryTestApp
.
In addition to the initial and maximum sizes of the memory allocation pool, the example displays the JVM arguments that it uses:
-Djava.library.path=C:\memexample\bundles\JavaFXMemoryTestApp\app\ -Dapp.preferences.id=MemoryTestAppID -Xms2048m -Xmx2048m
Note that the Ant build script uses <fx:jvmuserarg> elements to specify the initial and maximum sizes of the memory allocation pool as 31m and 64m, respectively. However, the Ant script in this example runs another program that stores the preferred values of 2048m and 2048m in the Preferences
node before the application is run, so those values are used instead.