Native packaging cookbook: drop-in resources

We are thrilled to see that developers are trying to use the new packaging option and appreciate all your feedback. Please keep it coming.

Official documentation on packaging self-contained applications (that's an official name for native packages) will be released with JavaFX 2.2 in few weeks. However, there are several questions that are frequent enough and I would like to answer them here instead of answering them in the forums and through email repeatedly.

They all boil down to the single generic question - what options do I have to customize result packages? Well, this question is too big to answer fully in one post, so the short answer is that many of existing packaging parameters are reused to produce a self-contained application package. For example <fx:preferences> can be used to request creation of desktop and menu shortcuts as well as choose between user level and system wide installation.

Not all options are obvious and properly hooked. For example using <fx:icon> to customize the application icon seems natural but will not be successful with JavaFX 2.2 due to oversight on our side. This will be fixed in future versions but there is a simple work around that should also work for other issues. The packaging tools use several built-in resources to produce a package, such as the application icon or config files. One way to customize the resulting package is to substitute built-in resources with your customized version.

For this you need to:

  • Know what resources are used
  • Drop custom resources into a location where the packaging tool will look for them
I'll show how to use this approach to solve couple of popular problems.

Customizing application icon

You can get more insight into the resources being used by enabling verbose mode by adding the verbose="true" attribute to <fx:deploy>, or pass the -v option to the javafxpackager -deploy command. This will output something like:

Using base JDK at: /Library/Java/JavaVirtualMachines/jdk1.7.0_06.jdk
  Using default package resource [Bundle config file] 
         (add package/macosx/Info.plist to the class path to customize)
  Using default package resource [icon]
         (add package/macosx/DemoApp.icns to the class path to customize) 

Now we know what name is used to look for "icon" (package/macosx/DemoApp.icns). Note that name is specific to application and operating system. Moreover, image formats are different on different platforms. You need .ico on windows, .icns on Mac and .png on Linux.

Where do we need to save our custom icon? If you are using javafxpackager or project created by NetBeans 7.2 then save it as ./package/macosx/DemoApp.icns, i.e. create this path relative to the project root directory. This should be enough. 

Packaging tools look for customized resources on the classpath before reverting to built-in resources. The javafxpackager utility and Netbeans 7.2 add "." to the classpath by default. If you are loading ant tasks on your own then simply add "." to the classpath yourself like:

<taskdef resource="com/sun/javafx/tools/ant/antlib.xml" 

If everything went well then verbose build output will change to report your custom resource is detected: 

 Using base JDK at: /Library/Java/JavaVirtualMachines/jdk1.7.0_06.jdk
  Using default package resource [Bundle config file]
        (add package/macosx/Info.plist to the class path to customize)
Using custom package resource [icon] 
        (loaded from package/macosx/DemoApp.icns on class path

To ensure that the icon is set in runtime, you also need to add it to the application stage. For example, add the following code to the start() method of your application:

stage.getIcons().add(new Image(this.getClass().getResourceAsStream("app.png")));

Fine tuning application bundle

If you are using packaging tools to produce an installable package there could be a need to tweak the application image before it is wrapped into the installer. Why? For example you may want to sign the application, so it does not appear to be untrusted to the OS (for example to please Mac OS X Gatekeeper).

Also by default a self-contained application does not contain full copy of Java Runtime. We only include set of mandatory components. Part of the reason why this approach was taken is that we want to reduce the package size. However, there are situations where your application may depend on these optional components and in that case you will need a way to add them to the private runtime. For example https connections will not work if jre/lib/ext/sunjce_provider.jar is missing.

Currently this can be achieved by providing a custom config script that is executed after application image is populated. Like in the example above with the icon, you need to enable verbose output to find the name of the script file and then drop it to the location where packaging tools will find it. Note that scripting language is platform specific too. Currently we only support shell for Mac/Linux and Windows Script on windows.

How do you find out where the application image is located? Currently custom scripts are run in the directory where config files are stored but application image can be accessed using relative platform specific path. You can derive this path from verbose output or by setting environment variable JAVAFX_ANT_DEBUG to true to keep intermediate build artifacts.

Here is sample script (contributed by John Petersen) you can use to add jre/lib/ext/sunjce_provider.jar to the application package of MyApp on the Windows platform. Script using Javascript but you could also use VBScript for Windows scripting.

<?xml version="1.0" ?>  
   <job id="postImage">  
    <script language="JScript">  
        var oFSO = new ActiveXObject("Scripting.FileSystemObject");  
        var oFolder = oFSO.getFolder(".");  
        var from = oFolder.path + "\\MyApp\\app\\sunjce_provider.jar";  
        var to = oFolder.path + "\\MyApp\\runtime\\jre\\lib\\ext";  
        if (!oFSO.FolderExists(to)) {  
        to += "\\";  
        oFSO.CopyFile(from, to);  

I don't see how that script can work, except in the unlikely case that sunjce_provider.jar is in your app's root directory -- I'd expect it to be used straight from the system jre. I got the script to work for me by changing that one line to be

var from = "C:\\Program Files\\Java\\jdk1.7.0_06\\jre\\lib\\ext\\sunjce_provider.jar";

On Mac OS, I had luck using the following for the contents of

cd ../images/dmg.image/
mkdir ext
cp -p /Library/Java/JavaVirtualMachines/jdk1.7.0_06.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar ext

Does anyone know what to use on a Debian package system? The verbose flag did not show me any place I could hook in a script before the packaging phase.

Posted by guest on August 17, 2012 at 07:04 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed



« July 2016