7u10: JavaFX packaging tools update

Posted by igor on Oracle Blogs See other posts from Oracle Blogs or by igor
Published on Tue, 25 Sep 2012 21:14:47 +0000 Indexed on 2012/09/26 3:45 UTC
Read the original article Hit count: 480

Filed under:

Last weeks were very busy here in Oracle. JavaOne 2012 is next week. Come to see us there!

Meanwhile i'd like to quickly update you on recent developments in the area of packaging tools. This is an area of ongoing development for the team, and we are  continuing to refine and improve both the tools and the process. Thanks to everyone who shared experiences and suggestions with us. We are listening and fixed many of reported issues. Please keep them coming as comments on the blog or (even better) file issues directly to the JIRA.

In this post i'll focus on several new packaging features added in JDK 7 update 10:

All these features and number of other important bug fixes are available in the developer preview builds of JDK 7 update 10 (build 8 or later). Please give them a try and share your feedback!

Self-Contained Applications: Select Java Runtime to bundle

Packager tools in 7u6 assume current JDK (based on java.home property) is the source for embedded runtime. This is useful simplification for many scenarios but there are cases where ability to specify what to embed explicitly is handy. For example IDE may be using fixed JDK to build the project and this is not the version you want to bundle into your application.

To make it more flexible we now allow to specify location of base JDK explicitly. It is optional and if you do not specify it then current JDK will be used (i.e. this change is fully backward compatible).

New 'basedir' attribute was added to <fx:platform> tag. Its value is location of JDK to be used. It is ok to point to either JRE inside the JDK or JDK top level folder. However, it must be JDK and not JRE as we need other JDK tools for proper packaging and it must be recent version of JDK that is bundled with JavaFX (i.e. Java 7 update 6 or later).

Here are examples (<fx:platform> is part of <fx:deploy> task):

   <fx:platform basedir="${java.home}"/>  
   <fx:platform basedir="c:\tools\jdk7"/> 
Hint: this feature enables you to use packaging tools from JDK 7 update 10 (and benefit from bug fixes and other features described below) to create application package with bundled FCS version of JRE 7 update 6.

Self-Contained Applications: Create Package without Java Runtime

This may sound a bit puzzling at first glance. Package without embedded Java Runtime is not really self-contained and obviously will not help with:
  • Deployment on fresh systems. JRE need to be installed separately (and this step will require admin permissions).
  • Possible compatibility issues due to updates of system runtime.
However, these packages are much much smaller in size. If download size matters and you are confident that user have recommended system JRE installed then this may be good option to consider if you want to improve user experience for install and launch.

Technically, this is implemented as an extension of previous feature. Pass empty string as value for 'basedir' attribute and this will be treated as request to not bundle Java runtime, e.g.
<fx:platform basedir=""/>

Self-Contained Applications: Package non-JavaFX application

One of popular questions people ask about self-contained applications - can i package my Java application as self-contained application? Absolutely.

This is true even for tools shipped with JDK 7 update 6. Simply follow steps for creating package for Swing application with integrated JavaFX content and they will work even if your application does not use JavaFX. What's wrong with it? Well, there are few caveats:
  • bundle size is larger because JavaFX is bundled whilst it is not really needed
  • main application jar needs to be packaged to comply to JavaFX packaging requirements
    (and this may be not trivial to achieve in your existing build scripts)
  • javafx application launcher may not work well with startup logic of your application 
    (for example launcher will initialize networking stack and this may void custom networking settings in your application code)

In JDK 7 update 6 <fx:deploy> was updated to accept arbitrary executable jar as an input. Self-contained application package will be created preserving input jar as-is, i.e. no JavaFX launcher will be embedded. This does not help with first point above but resolves other two.

More formally following assertions must be true for packaging to succeed:

  • application can be launched as "java -jar YourApp.jar" from the command line 
  • mainClass attribute of <fx:application> refers to application main class
  • <fx:resources> lists all resources needed for the application

To give you an example lets assume we need to create a bundle for application consisting of 3 jars:
    dist/javamain.jar
    dist/lib/somelib.jar
    dist/morelibs/anotherlib.jar

where javamain.jar has manifest with
     Main-Class: app.Main
     Class-Path: lib/somelib.jar morelibs/anotherlib.jar


Here is sample ant code to package it:

<target name="package-bundle"> 
      <taskdef resource="com/sun/javafx/tools/ant/antlib.xml"  
                uri="javafx:com.sun.javafx.tools.ant"    
                classpath="${javafx.tools.ant.jar}"/> 
      <fx:deploy nativeBundles="all"  
                   width="100" height="100" 
                   outdir="native-packages/" outfile="MyJavaApp">  
            <info title="Sample project" vendor="Me"  
                     description="Test built from Java executable jar"/>  
            <fx:application id="myapp" version="1.0"  
                  mainClass="app.Main" name="MyJavaApp"/>  
            <fx:resources>  
               <fx:fileset dir="dist">  
                  <include name="javamain.jar"/>  
                  <include name="lib/somelib.jar"/>  
                  <include name="morelibs/anotherlib.jar"/>  
               </fx:fileset>  
            </fx:resources> 
      </fx:deploy>  
</target>

Option to disable proxy setup in the JavaFX launcher

Since JavaFX 2.2 (part of JDK 7u6) properly packaged JavaFX applications  have proxy settings initialized according to Java Runtime configuration settings.

This is handy for most of the application accessing network with one exception. If your application explicitly sets networking properties (e.g. socksProxyHost) then they must be set before networking stack is initialized. Proxy detection will initialize networking stack and therefore your custom settings will be ignored.

One way to disable proxy setup by the embedded JavaFX launcher is to pass "-Djavafx.autoproxy.disable=true" on the command line. This is good for troubleshooting (proxy detection may cause significant startup time increases if network is misconfigured) but not really user friendly.

Now proxy setup will be disabled if manifest of main application jar has "JavaFX-Feature-Proxy" entry with value "None". Here is simple example of adding this entry using <fx:jar> task:
<fx:jar destfile="dist/sampleapp.jar"> 
     <fx:application refid="myapp"/> 
     <fx:resources refid="myresources"/> 
     <fileset dir="build/classes"/> 
     <manifest>  
            <attribute name="JavaFX-Feature-Proxy" value="None"/>  
     </manifest>      
</fx:jar>

Ability to specify codebase for WebStart application

JavaFX applications do not need to specify codebase (i.e. absolute location where application code will be deployed) for most of real world deployment scenarios. This is convenient as application does not need to be modified when it is moved from development to deployment environment.

However, some developers want to ensure copies of their application JNLP file will redirect to master location. This is where codebase is needed. To avoid need to edit JNLP file manually <fx:deploy> task now accepts optional codebase attribute. If attribute is not specified packager will generate same no-codebase files as before.

If codebase value is explicitly specified then generated JNLP files (including JNLP content embedded into web page) will use it.  Here is an example:

<fx:deploy width="600" height="400"  
                  outdir="Samples"  
                  codebase="http://localhost/codebaseTest"  
                  outfile="TestApp">  
        .... 
</fx:deploy>

Option to update existing jar file

JavaFX packaging procedures are optimized for new application that can use ant or command line javafxpackager utility. This may lead to some redundant steps when you add it to your existing build process.

One typical situation is that you might already have a build procedure that produces executable jar file with custom manifest. To properly package it as JavaFX executable jar you would need to unpack it and then use javafxpackager or <fx:jar> to create jar again (and you need to make sure you pass all important details from your custom manifest).

We added option to pass jar file as an input to javafxpackager and <fx:jar>. This simplifies integration of JavaFX packaging tools into existing build  process as postprocessing step. By the way, we are looking for ways to simplify this further. Please share your suggestions!

On the technical side this works as follows. Both <fx:jar> and javafxpackager will attempt to update existing jar file if this is the only input file. Update process will add JavaFX launcher classes and update the jar manifest with JavaFX attributes. Your custom attributes will be preserved. Update could be performed in place or result may be saved to a different file.

Main-Class and Class-Path elements (if present) of manifest of input jar file will be used for JavaFX application  unless they are explicitly overriden in the packaging command you use. E.g. attribute mainClass of <fx:application> (or -appclass in the javafxpackager case) overrides existing Main-Class in the jar manifest. Note that class specified in the Main-Class attribute could either extend JavaFX Application or provide static main() method.

Here are examples of updating jar file using javafxpackager:
  • Create new JavaFX executable jar as a copy of given jar file 
    javafxpackager -createjar -srcdir dist -srcfiles fish_proto.jar -outdir dist -outfile fish.jar 
  • Update existing jar file to be JavaFX executable jar and use test.Fish as main application class 
    javafxpackager -createjar -srcdir dist -appclass test.Fish -srcfiles fish.jar -outdir dist -outfile fish.jar 
And here is example of using <fx:jar> to create new JavaFX executable jar from the existing fish_proto.jar:
<fx:jar destfile="dist/fish.jar">  
     <fileset dir="dist">  
        <include name="fish_proto.jar"/>  
     </fileset>  
 </fx:jar>  

Self-Contained Applications: Specify application icon

The only way to specify application icon for self-contained application using tools in JDK 7 update 6 is to use drop-in resources.

Now this bug is resolved and you can also specify icon using <fx:icon> tag. Here is an example:

<fx:deploy ...> 
     <fx:info> 
         <fx:icon href="default.png"/> 
     </fx:info> 
     ... 
</fx:deploy>
Few things to keep in mind:

  • Only default kind of icon is applicable to self-contained applications (as of now)
  • Icon should follow platform specific rules for sizes and image format (e.g. .ico on Windows and .icns on Mac)

Self-Contained Applications: Pass parameters on the command line

JavaFX applications support two types of application parameters: named and unnamed (see the API for Application.Parameters).

Static named parameters can be added to the application package using <fx:param> and unnamed parameters can be added using <fx:argument>. They are applicable to all execution modes including standalone applications.

It is also possible to pass parameters to a JavaFX application from a Web page that hosts it, using <fx:htmlParam>.  Prior to JavaFX 2.2, this was only supported for embedded applications. Starting from JavaFX 2.2, <fx:htmlParam> is applicable to Web Start applications also. See JavaFX deployment guide for more details on this.

However, there was no way to pass dynamic parameters to the self-contained application. This has been improved and now native launchers will  delegate parameters from command line to the application code. I.e. to pass parameter to the application you simply need to run it as "myapp.exe somevalue" and then use getParameters().getUnnamed().get(0) to get "somevalue".

© Oracle Blogs or respective owner

Related posts about /Oracle