Introduction

jDeploy allows Java developers to deploy desktop applications as native bundles on Macintosh, Windows, and Linux. Unlike other deployment solutions, jDeploy doesn’t require any third party tools (other than OpenJDK), and can build native installers for Mac, Windows, and Linux on any platform. For example, you can build a native Windows installer on Linux or Mac, and vice versa. Applications deployed using jDeploy can also receive updates automatically as they become available, so you can be assured that your users will always be working with the latest version of your application.

Note
Apps distributed with jDeploy can also be installed as command-line apps using npm. See Command-line Distribution for more information about that.

GUI

jDeploy provides a graphical user interface that makes it easy to configure your app’s deployment settings such as icons, splash screens, file associations, etc…​ After you’re satisfied with the settings, press "Publish", and it will publish your app so that users can download the latest version.

details tab
Figure 1. The jDeploy GUI allows you to configure and publish your app.

IntelliJ Plugin

If you use IntelliJ IDEA for your IDE, then you may prefer to use the jDeploy IntelliJ Plugin, which provides a project wizard with plug-and-play templates for Swing, Codename One, and JavaFX projects.

Download Page

When you publish your application using jDeploy, your users will instantly be able to download your app at https://www.jdeploy.com/~YOUR-APP-NAME

swingset download page
Figure 2. Example download page the Java SwingSet demo app.

The download page includes links for Windows, Mac, and Linux. At time of writing, Linux includes two different download types: .deb, and .bin, but more may be added as there is demand.

Note
You can also use the Github Action to add your installers as artifacts on your app’s release page.

The Installer

The installer that is downloaded from the download page will prompt the user to select relevant installation options, such as whether to add a link in the Dock (Mac only), or the Start Menu (Windows Only), and whether to enable auto-update (default "On").

swingset installer mac
Figure 3. SwingSet2 Installer on Mac

After selecting the desired options, the user can press the Install button, which will trigger the installation of your app. This will only take a second, and, when complete, the user will be prompted with a dialog as follows:

swingset success mac
Figure 4. Dialog shown after successful installation.

In this example, clicking on the "Open SwingSet2", will open the app as shown below:

swingset mac
Figure 5. The SwingSet2 Demo application, running on Mac OS

Mac Codesigning and Notarization

When you deploy your app with jDeploy, you don’t need to sign or notarize it as you normally would when deploying a Mac application. jDeploy handles all of this so you don’t need to worry about it. You don’t require any Apple developer certificates; nor do you require an Apple developer account. Mac users running the latest versions of MacOS will be able to download and install your app, by simply downloading it from the jDeploy download page, and running the installer.

If, however, you do have an Apple Developer account, and want to sign and notarize your app, you can do this as well, via a convenient Github action.

Java Versions

jDeploy currently uses Azul’s OpenJDK REST API to download an appropriate JRE, or JDK on demand. You can specify which version of Java your app requires in your jDeploy config file. You can also specify whether your app requires a full JDK, or JavaFX. Deploying JavaFX apps with jDeploy is actually quite painless - as you don’t need to bundle the JavaFX jars or modules with your distribution.

At time of writing, LTS (long-term-support) Java versions from 8 through 17 are supported.

GitHub Action

You can automate your deployments using the jDeploy Github action. Publish your app as a GitHub release, or using npm’s repository.

Publish to npm and/or GitHub

jDeploy supports hosting your releases on both GitHub (as release artifacts) or npm. When you publish a new version, your app will automatically detect this the next time it is launched, so that you can be sure that your users are always working with your latest version.

Important
Publishing the app to npm requires an NPM account. This takes a few seconds to set up. You can simply type npm adduser and follow the prompts. full instructions on the NPM site. If you already have an account but haven’t logged in on the current machine you would use npm login to login and store the credentials locally.

Git Branch Synchronization

The jDeploy GitHub action will allow you to publish native apps that stay in-sync with changes in a particular branch of your repository. When you commit changes to that branch, the app will automatically receive the update the next time it is launched. This all happens through the magic of Github actions.

Getting Started

Requirements

JDK

In order to run jDeploy, you need to have JDK 8 or higher installed on your development machine. Several free distributions are available including Adoptium (formerly AdoptOpenJDK), Zulu, and Corretto.

NodeJS

jDeploy uses npm for its distribution because it simplifies the installation process dramatically. npm comes packaged with NodeJS, which can be downloaded here.

Tip
We recommend using the latest version of NodeJS that is available, as that will ensure compatibility. At time of writing, we are using NodeJS 14.15.4 and npm 8.3.0 (which is bundled with NodeJS 14.15.4) for our testing.
Important
NodeJS is not required if you are using the jDeploy IntelliJ Plugin.
npm Account
Important
An npm account is not required if you are using GitHub releases for your deployment.

If you want to be able to publish your apps for others to use, you will also need an npm account. npm accounts are free, and it only takes a minute to create one. The signup page is here.

Installation

Tip
If you use IntelliJ IDEA, you might prefer to use the jDeploy IntelliJ Plugin instead of installing the jDeploy via CLI.

You can either install jdeploy globally, or locally. When you install it locally, it will only be available to the project that you install it into, and you would run it with npx jdeploy …​. When you install it globally, it will be available in your command-line path no matter where you are.

Installing Globally

npm install -g jdeploy
Important
On Mac and Linux you’ll need to use sudo to install globally. E.g. sudo npm install -g jdeploy

Installing Locally

cd /path/to/your/project
npm install jdeploy
Important
When you install it locally, it will create a directory named "node_modules" in the current directory, and jdeploy will be installed there. To run jdeploy, you would need to be in the project directory, and you need to use npx to run it. E.g. npx jdeploy …​.

Usage

Open a command prompt and navigate to your Java project directory.

cd path/to/myproject

Run the jdeploy command. E.g.

jdeploy

If this is your first time using jdeploy on this package, you’ll be prompted to create a package.json file:

create package json prompt

Press Yes.

This will open the jDeploy GUI, and it will look something like the following screenshot.

details tab

jDeploy will have already tried to generate some sensible settings here, but you should go over them yourself to make sure they are correct.

Tip
See the GUI help page for a thorough overview of the user interface.

The most important fields here are:

Name

The unique name of your app. This is used for publishing in the npm registry and must be unique in npm. You may want to search on npmjs.com to make sure that your name hasn’t been taken yet.

Version

The version of your app. This is used for publishing to npm. Each time you publish, you should increment the version number.

Title

The user-friendly title for your app. This is used in your app name, installer name, and download page.

Jar File

The main jar file for your app. This must be an executable jar file. See Appendix: Building Executable Jar File.

Icon

The icon for your app. This should be a 512x512 PNG with transparency.

Publishing Your App

Tip
You can publish your app to either npm or Github releases (or both, if you like). Use the jDeploy github action to publish to Github releases. The following section describes the process to publish on npm.

Once you have configured your app to your liking, press the Publish button.

You might be prompted to log into npm if you haven’t already.

You will be shown a progress dialog with a readout of npm’s progress.

publish in progress

If it is successful, you’ll see a "Success Message" like the following:

publish success

As soon as you see this message, your app is live and available to download on your download page.

Command-line Usage

If you eschew GUIs and prefer to work on the command-line, the following instructions are for you.

In terminal, navigate to a directory containing an executable .jar file that you would like to publish.

$ jdeploy init

This will generate a package.json file with settings to allow you to publish the app to npm.

The package.json file

After this command completes, you should open the package.json in your text editor to adjust the settings to your requirements. The following is the package.json file for the SwingSet2 app.

{
  "bin": {"swingset2": "jdeploy-bundle/jdeploy.js"},
  "author": "Oracle",
  "description": "The Swing Sampler demo, packaged by jDeploy",
  "main": "index.js",
  "preferGlobal": true,
  "repository": "https://github.com/shannah/swingset2",
  "version": "1.0.5",
  "jdeploy": {
    "jar": "target/swingset2-1.0-SNAPSHOT.jar",
    "javaVersion" : "11",
    "title" : "SwingSet2",
    "javafx" : false,
    "jdk" : false
  },
  "dependencies": {"shelljs": "^0.8.4", "njre": "^0.2.0"},
  "license": "GPLv2+Classpath Exception",
  "name": "jdeploy-demo-swingset2",
  "files": ["jdeploy-bundle"],
  "scripts": {"test": "echo \"Error: no test specified\" && exit 1"}
}

The package.json specification can be found here. The jdeploy object includes additional properties that you can use to customize how your native desktop bundle. It includes the following keys:

jar

The path (relative to the package.json file) to your executable jar file. It the jdeploy init command worked correctly, it should already point to the correct jar file. If it found the wrong file, or the name of the file changes later, you may need to adjust this value.

Type: String

javaVersion

The Java runtime version required to run your app. At time of writing the default is "11". You can also use "8", or "17" here. In theory any version that Azul's API supports can be used here.

Important
This value should not include only the major version. E.g. "11", NOT "11.0". Also, this value should be a string, not an int. I.e. "javaVersion" : "11", NOT "javaVersion" : 11.

Type: String

title

Your app’s title. This is the human-readable name of your app. It will be used on the download page, and also as the app’s file name when downloaded.

Type: String

jdk

Whether the app requires a full JDK to run. If true, then the app will be run with a JDK - not just the JRE. Unless your app actually needs a JDK (i.e. access to javac, etc…​), you should keep this false as the JDK is substantially larger than the JRE.

Type: boolean

javafx

Whether your app requires JavaFX. If true, then the app will run with a JRE/JDK that includes JavaFX.

Type: boolean

Icons and Splash Images

There are three specially-named images that, if placed in the same directory as your package.json, will be used by jDeploy. They are:

icon.png

An icon for your app. If you don’t include this file, it will use a generic jDeploy icon for your app.

Recommended specifications: 512x512 pixels, with transparency.

Note
If you are using JavaFX you should also add your icon to your app’s Stage using stage.getIcons().add(…​) as there is a bug in Ubuntu that will cause the app’s icon to appear blank in the taskbar otherwise.
splash.png/splash.jpg/splash.gif

A splash image to be displayed while your app is loading.

Note
jDeploy uses the -splash:splash.gif CLI argument at runtime to add the splash screen. This apparently causes some issues with JavaFX. Currently, the recommendation for adding splash screens to JavaFX apps is to use a Preloader. Example here.
installsplash.png

A splash/info screen to be displayed in the installer for your app.

Publish App to NPM

Tip
This section describes how to publish to npm, but you can alternatively publish to Github releases using the jDeploy github action.
Important
In order to publish your app with jDeploy, you need to have an npm account. For instructions on creating your free npm account see this tutorial.

You must be logged into your npm account in the command-line in order for the jdeploy publish command to work. If you already have an npm account you can login with the npm login command. See this tutorial for more details.

Once you have logged into your npm account and configured your package.json file to your liking, you can publish your app to npm with the following command:

$ jdeploy publish

If it completes successfully, you’ll be able to download your app bundles at https://www.jdeploy.com/~YOUR-APP-NAME where YOUR-APP-NAME is the name specified in the name property of your package.json file.

Publishing New Versions

If you make changes to your app, and want to publish a new version, you can increment the version property of your package.json file, and run jdeploy publish again.

File Associations

jDeploy allows you to associate your application with file mimetypes and extensions. For example, you can associate your application with .txt files so that it is listed as one of the options when users right click on a .txt file and select "Open with". Additionally, your application will respond when text files are dropped onto your app icon.

You can add file associations in the "Filetypes" tab of the GUI. Alternatively see File Associations In package.json for a description of how to add the associations directly in the package.json file.

filetypes tab

To add a new document type, press the add button button. To remove a document type, press the delete icon button in the corresponding row.

Each file type row includes the following fields:

Extension

The file extension. E.g. txt, html, mp4. Do not include the "dot".

Mimetype

The corresponding mimetype of the extension. You should provide both the extension and mimetype in each row, as some operating systems rely more heavily on one than the other.

Editor

Check this box if your app can edit files of this type. Leave it unchecked if it can only view files of this type.

Custom

Check this box if this is a custom mimetype. This is used on Linux as an indicator that the installer needs to register the mimetype in the system’s mimetype database.

File Associations In package.json

Such file associations can registered by adding the documentTypes property to the jdeploy object. E.g.

...
"jdeploy" : {

    ...
    "documentTypes" : [
      {
        "extension" : "txt",
        "mimetype" : "text/plain",
        "editor" : true
      }, {
        "extension" : "html",
        "mimetype" : "text/html"
      }, {
        "extension" : "jdtext",
        "mimetype" : "application/x-jdeploy-demo-texteditor-jdtext"
      }, ....
    ],
}
...

The documentTypes array may contain zero of more object entries, with the following properties:

extension

The extension of the file type. E.g. txt, html, etc…​

mimetype

The mimetype of the file type. E.g. text/plain, text/html, etc…​

editor

Boolean indicating whether the application can edit the file type. If omitted or false, the application will be regarded as a "viewer" for this file type.

custom

Boolean indicating whether this is a custom file type for your app. This is currently only used for .deb installers so that it knows whether it needs to register the mimetype with the system. Default is false

Accessing Files In Java

When a user launches your app by opening an associated file type (e.g. by dragging the file on your app icon, or selecting your app in the "Open with" menu), you will likely want to know which file triggered the launch. On Windows and Linux, these file paths are passed as arguments in your main() method. E.g.

On Linux and Windows opened files can be accessed in your main args.
public static void main(String[] args) {

    if (args.length > 0) {
        System.out.println("Received "+args.length+" file arguments");
        for (int i=0; i<arg.length; i++) {

            System.out.println("File: "+args[i]);
        }
    }
}

On Macintosh, you need to use the java.awt.Desktop class to be notified when the user opens a file with your app.

Using the FileHandler interface with the Desktop class to be notified when users open files with your app on Mac OS.
import java.awt.*;
import java.awt.desktop.OpenFilesHandler;
import java.awt.desktop.OpenFilesEvent;

public class HelloApplication {
    static {
        try {
            Desktop.getDesktop().setOpenFileHandler(new FileHandler());
        } catch (Exception ex){}
    }

    ...

    public static class FileHandler implements OpenFilesHandler {

        @Override
        public void openFiles(OpenFilesEvent e) {
            System.out.println("Received open files event "+e.toString());
        }
    }

    ...
}

URL Schemes

jDeploy allows you to register your application to open URLs with specific schemes so that, when a user clicks on a link with that URL scheme, your application will be launched.

You can add URL schemes in the URLs tab of the GUI. To add url schemes directly in the package.json file, see URL Schemes in package.json.

For example, you if you specify the "myapp" URL scheme, then you could add a link on your website as follows:

<a href="myapp:some-data">Open My App</a>

When users who have your app installed click on this link, they will be prompted to launch your app. When you app launches, you’ll be able to detect the specific URL that was clicked, so that you can respond appropriately.

The URLs tab is shown here:

urls tab

In the above example, the app would be associated with URLs like "jdtext:xxxxxx".

Adding Multiple URL Schemes

To add multiple URL schemes, you can simply add multiple values separated by commas in the text field.

e.g.

"myapp,podcast,feed"

In this case the app would respond to URLs like "myapp:…​", "podcast:…​", and "feed:…​"

Standard URL Schemes

You can associate your app with both custom schemes, and standard schemes. For a list of standard URL schemes that you might want your app to be ableto handle, see this wikipedia entry.

URL Schemes in package.json

Do this by adding a urlSchemes property of the jdeploy object with an array of string schemes.

E.g.:

"jdeploy" : {

  ...
  "urlSchemes" :  ["jdtext"]
  ...
}

With the above configuration in the package.json file, your app would be launched when the user clicks links like "jdtext:…​." in a webpage.

E.g.

Example HTML with link that will open in your app, assuming you have registered the "jdtext" URL scheme.
...
<p>Testing the custom jdtext url scheme.  <a href="jdtext:hello">CLick here</a></p>
...

Accessing URLs In Java

When a user launches your app by clicking on a link, you will likely want to know the URL that triggered the launch. On Windows and Linux, these URLs paths are passed as arguments in your main() method. E.g.

On Linux and Windows opened files can be accessed in your main args.
public static void main(String[] args) {

    if (args.length > 0) {
        System.out.println("Received "+args.length+" URL arguments");
        for (int i=0; i<arg.length; i++) {

            System.out.println("URL: "+args[i]);
        }
    }
}

On Macintosh, you need to use the java.awt.Desktop class to be notified when the user opens a URL with your app.

Using the URIHandler interface with the Desktop class to be notified when users open URLs with your app on Mac OS.
import java.awt.Desktop;
import java.awt.desktop.OpenURIEvent;
import java.awt.desktop.OpenURIHandler;

public class HelloApplication {
    static {
        try {
            Desktop.getDesktop().setOpenURIHandler(new URIHandler());
        } catch (Exception ex){}
    }

    ...

    public static class URIHandler implements OpenURIHandler {

        @Override
        public void openURI(OpenURIEvent e) {
            System.out.println("Received open uri event "+e.toString());
        }
    }

    ...
}

Homepage Verification

When users install your app, they will be prompted with a warning dialog to remind them to only install software from trusted sources. They are encouraged to review the app’s homepage to ensure that they trust the developer before proceeding with the installation. The dialog will look something like:

trust prompt dialog

By default it will report the software’s verified homepage to be the npmjs.org package page for your app. If you want this to report your actual website or GitHub repository, you’ll need to enter your your website URL into the "Homepage" field the "Details" tab, and then verify ownership.

homepage field

To verify ownership of this page, you’ll need to copy and past your app’s SHA256 hash code into your website. You can begin this process by pressing the "Verify" button beside the Homepage text field. It will display a dialog like the following:

verify homepage dialog

Click on the SHA256 hash code in this dialog to have it copied to the clipboard. Then paste it somewhere in your webpage. This can be hidden inside a meta tag, or just placed into the text of the page. As long as the jDeploy installer can make an HTTP request to your homepage and find the code amongst the page content.

Important
The sha256 hash code is generated based on the package name and the homepage URL, so if you change the homepage URL, the hash will change, and you’ll have to re-verify.

Once you have added the code to your homepage, press the "Verify" button so that jDeploy can check it. If everything looks good, you’ll see a success dialog like the following:

homepage verified
Important
This verification step is just a sanity check to ensure that you’ve pasted the code correctly. It doesn’t store this verification anywhere, and the jDeploy installer will perform its own check at install time whenever a user installs your app. This means that you shouldn’t remove the code after verification. You should leave it there permanently - or at least as long as you want your installers to be able to verify to your homepage.

Command-line Distribution

Apps distributed using jDeploy can also be installed in the command-line using npm. When distributing in this way, you’ll want to pay attention to the bin and name properties of your package.json file.

name

This is the name of our project as it will be listed in the NPM registry. This name must be unique. If you try to publish a project with a name that is already registered by someone else, it will fail. This name will be used by users who want to install your app. E.g. If "name"= "hello-world", then people would install my app by typing "npm install hello-world -g" at the command prompt.

bin

This specifies the name of the command that will be installed to activate your app. It actually maps an alias to the "jdeploy-bundle/jdeploy.js" script, which is thin bootstrap shell script for your java app. The entry will look like "bin" : {"hello-world" : "jdeploy-bundle/jdeploy.js"}. If you want to change your app’s command name to "my-hello-world", then you would just change "hello-world" to "my-hello-world".

You don’t need to do anything special to allow your app to be installed via the command-line. If you have published it using the jdeploy publish command, then, assuming your package name is "hello-world", users will be able to install your app by typing:

$ npm install hello-world -g

Some things to note:

  1. On Mac and Linux you’ll need to use sudo as it adds a symlink to /usr/local/bin which requires sudo permissions. On Windows it will work without sudo as long as you are using an Administrator account.

  2. If you omit the -g it will install the app locally (i.e. for use inside the current working directory). When installed in this way, you need to use the npx command to run your app, and it will only work when run inside the install directory.

They can then launch your app with:

$ hello-world

or, if they installed it locally, they would instead run it with:

$ npx hello-world

See Getting Started with jDeploy for Command-line apps for step-by-step instructions in deploying your command-line app.

Runtime Arguments

jDeploy allows you to configure JVM options, system properties, and program arguments for your application in the Runtime Args tab of the GUI, or the args property of the jdeploy object in the package.json file.

When working in the GUI, you should enter the arguments one per line in the provided text area. jDeploy supports a small set of placeholder variables that you can include in your arguments, that will be replaced at runtime. It also supports a minimal syntax for making platform-specific arguments, which are used on some platforms and not others.

runtimeargs tab
Figure 6. A sample Runtime Args tab demonstrating how to set a few JVM options, properties, and program arguments.

The above arguments would be saved in the package.json file as follows:

{
    ...
    "jdeploy": {
        ...
        "args": [
            "-Dmyapp.foo=bar",
            "-Xmx2G",
            "-D[mac]application.support.dir={{ user.home }}/Library/Application Support",
            "-D[mac]resources.dir={{ app.path }}/Contents/Resources",
            "gui"
        ],
        ...

JVM Options

JVM options are options that configure how the JVM works. They are always marked with the "-X" prefix. Some examples of commonly-used JVM options include:

-XmsXXX

Sets the minimum and initial size of the heap. E.g. -Xms2G. See the Java documentation for more information.

-XmxXXX

Sets the maximum sizeof the heap. E.g. -Xmx4g. See the Java documentation for more information.

System Properties

You can set system properties using the -D prefix. E.g. -Dmyapp.setting=foobar

Program Arguments

Arguments that are not prefixed with -X or -D are treated as program arguments. They will be passed to your program, and you can receive them in your main(String[] args) method.

Placeholder Variables

jDeploy supports a handful of placeholder variables that can be embedded in your run arguments, and will be replaced with the appropriate value at runtime. Placeholder variables are marked with {{ varname }}. The available variables are as follows:

{{ user.home }}

The path to the user’s home directory.

{{ exe.path }}

The path to the executable.

{{ app.path }}

The path to the .app bundle (when run on Mac). Otherwise it will be the same as {{ exe.path }}.

Platform-Specific Arguments

In some cases you may want a setting to only be applied on a specific platform. For example some JVM options may be mac specific. jDeploy provides a minimal syntax for marking an argument as platform-specific. For system properties, you would add a condition after the -D prefix, so instead of -Dfoo=bar, you would have -D[CONDITION]foo=bar where CONDITION is one or more platform names ("mac-x64", "mac-arm64", "linux", "win") delimited by pipes. Some examples:

-D[mac]foo=bar

Same as -Dfoo=bar, but only on Mac.

-D[win]foo=bar

Same as -Dfoo=bar, but only on Windows.

-D[win|linux]foo=bar

Same as -Dfoo=bar, but only on Windows and Linux.

A similar syntax is used for platform-specific JVM options: -X[CONDITION]value, where CONDITION is one or more platform names ("mac-x64", "mac-arm64", "linux", "win") delimited by pipes. Some examples:

-X[mac]ms2G

Same as -Xms2G, but only on Mac.

-X[mac|linux]mx4G

Same as -Xmx4G, but only on Mac and Linux.

Finally, for program arguments (i.e. arguments that are passed to your main(args) method), you can mark them as platform-specific by adding a -[CONDITION] prefix. E.g. -[mac]anArgOnlyAddedOnMac. Some more examples:

-[mac]foo

Same as foo, but only on Mac.

-[linux|win]foo

Same as foo, but only on Linux and Windows.

Publishing as Web App (CheerpJ)

jDeploy includes experimental support for deploying Java Swing/AWT-based applications as progressive web apps. From the CheerpJ website:

CheerpJ is a WebAssembly-based Java Virtual Machine for the browser. It has extensive compatibility with Java 8 and provides a full runtime environment1 for running Java applications, applets, libraries, and Java Web Start / JNLP applications in the browser without plugins.

This is currently experimental. Limitations include:

  1. Only Swing/AWT UIs are supported currently. No JavaFX.

  2. Some 3rd-party java libraries may not be supported. You can try to build and find out.

Important
Please read the CheerpJ licensing options before deploying your application, to ensure that you are fully compliant.

To enable CheerpJ support, you should add a "cheerpj" property to the "jdeploy" object in the package.json file. E.g.:

"jdeploy": {
       ...
        "cheerpj": {
            "githubPages": {
                "branchPath": "{{ branch }}",
                "tagPath": "app",
                "branch": "gh-pages",
                "enabled": true
            },
            "enabled": true
        },
    },
...

The above configuration will be used by the github action to publish a web application via github pages.

The following properties, pertaining to Cheerpj, are available.

jdeploy.cheerpj

Boolean value indicating whether the github action should attempt to build the app as a web app using CheerpJ.

jdeploy.cheerpj.githubPages

Object with configuration properties for GitHub pages.

jdeploy.cheerpj.githubPages.enabled

Whether to publish to GitHub pages. Boolean.

jdeploy.cheerpj.githubPages.branch

The branch to use for publishing to GitHub pages. This should match the branch that is configured for GitHub pages on GitHub.

jdeploy.cheerpj.githubPages.branchPath

The path within the pages site where the app should be published for "branch" commits. You can use the {{ branch }} placeholder to include the name of the branch. It will be replaced by the name of the branch on which the commit occurred.

jdeploy.cheerpj.githubPages.tagPath

The path within the pages site where the app should be published for "tag" releases. You can use the {{ tag }} placeholder to include the name of the tag in the path. It will be replaced by the name of the tag.

jdeploy.cheerpj.githubPages.path

Optional. If you want branch commits and tag commits to be published to the same path, then you can use this to set them both in one place.

Tip
The jDeploy IntelliJ Plugin includes a GUI form for configuring CheerpJ in the jDeploy Settings dialog.
cheerpj tab

Building Bundles Locally

Important
When building your bundles locally, you still need to ensure that you run jdeploy publish to publish your app on npm, as the launcher relies on your published package for the automatic updates feature.

The easiest way to distribute your app is to just share the link to its download page at https://www.jdeploy.com/~your-app-name, however, you can optionally just generate your bundles locally as part of the jdeploy package step (which is included when you run jdeploy publish).

There are two flavors of packages that you can create:

bundles

The .exe, .app, etc.. bundles that launch your app directly.

installers

The installers that you can distribute to your users which install your .exe/.app, etc…​ bundles.

If you want to distribute your app to users, the installers provide a better experience since they include options to, for example, add your app to the programs menu (Windows/Linux) or the Dock (Mac).

You can generate bundles via the bundles property of the jdeploy object in your package.json file. E.g.

{
  ...
  "jdeploy" : {
    ...
    "bundles": ["mac-x64", "mac-arm64", "win", "linux"],
  }

}

With this setting the jdeploy package command (and jdeploy publish) will generate your app bundles in the jdeploy/bundles directory. If you only wanted to generate a "mac" bundle for Intel macs, you would do:

"bundles" : ["mac-x64"]

To generate installers, you can add the "installers" property. E.g.

"installers" : ["mac-x64", "mac-arm64", "win", "linux"]

This would result in installer bundles being generated in the jdeploy/installers directory.

Note
The installers generated in this way may differ slightly from the installers available on your download page. This is because the download page uses a different mechanism for generating the packages. Additionally, the download page may provide some installer types that aren’t available locally.

Publishing on GitHub

Important
You must publish your releases to a public repository, or your app installer won’t be able to download updates. If your repository is private, you can use the target_repository parameter for the GitHub action to publish your installers to a different repository that is public.

As of jDeploy 4.0, you can deploy your application on GitHub packages instead of npm. When deploying bundles in this way, you do not require an npm account. Some of the features of this deployment style include:

  1. GitHub actions integration to automate deployments on push/release/tag.

  2. Publish app artifacts in the GitHub release itself.

  3. Branch-App Synchronization: Distribute app bundles that are automatically synchronized with a specific branch of your repo.

  4. Multiple Streams: Distributed multiple versions of your app (e.g. "dev", "stage", "production") that are synchronized to different branches of your repository.

  5. Still includes support for jDeploy Download page. In addition to publishing apps as artifacts of GitHub releases, your bundles will still be available at jdeploy.com on your download page.

Publishing Releases with GitHub Actions

Tip
The jDeploy IntelliJ Plugin provides a project wizard that includes project templates for Codename One, Swing, and JavaFX, which are plug-and-play, including full support for publishing releases using GitHub actions.

To publish your packages on GitHub, you’ll need to add the following to your workflow.

- name: Publish with jDeploy
  uses: shannah/jdeploy@master
  with:
    github_token: '${{ github.token }}'

This should be placed in a step after you have built your app already (e.g. using maven, gradle, ant, etc…​).

If this action is used in a workflow on a merge or push, then it will publish artifacts on a release named after the branch that the workflow is running on.

E.g. If this workflow runs on the "master" branch, then it will publish your app bundles as artifacts on a tag named "master".

If this action is used in a workflow on a release or tag action, then it will add the app bundles as artifacts directly on the release/tag in GitHub.

Tip

You can add a working workflow to your app using the jDeploy GUI, by selecting "File" > "Add Github Workflow"

github add workflow

Your App Download Page

The URL convention for the jDeploy download page for apps hosted on GitHub is:

https://www.jdeploy.com/gh/YOUR_GITHUB_ACCOUNT/YOUR_REPO_NAME

E.g. The SwingSet app whose repository is located at https://github.com/shannah/swingset2, will have download page at https://www.jdeploy.com/gh/shannah/swingset2.

To access the download page for a specific branch you can append the branch name to the URL. E.g. The download for the "Swing Set 2 (master)" app, which is synchronized with the "master" branch, the URL would be:

https://www.jdeploy.com/gh/shannah/swingset2/master

Distributing Mac App as a DMG image

By default, MacOS apps are distributed as an installer, which is signed and notarized by jDeploy, so you don’t need to sign it with your own Apple developer certificate. However, if you would prefer to sign the app with your own certificate, you can distribute it as a DMG instead of an installer, you can do this via the jDeploy DMG GitHub action.

The IntelliJ plugin project templates include a DMG job in the GitHub workflow by default, but you do need to define a configuration variable and some secrets in your repository.

For full instructions, see the README.

Publishing Releases for Private Repositories

Your release artifacts must be published to a public repository in order for the jDeploy installer to be able to download updates for your app. If your repository is private, you can publish your releases in a different repository which is public.

Step One: Create a Public Repository for your Releases

For example, consider a scenario where your app is in the private repository yourname/yourapp. Then you could create a new public repository at yourname/yourapp-releases.

Important
Make sure you make at least one commit to this repo before publishing releases to it, or GitHub won’t allow you to add a release. E.g. Add a readme file to the repo.

Step Two: Create a Personal Access Token

In order to publish releases to a different repository, you’ll need to create a personal access token that has permission to create releases in the other repository.

Once you’ve created the personal access token, you’ll need to add it as a secret in your repository.

Step Three: Specify the target_repository in your workflow

Finally, you’ll need to modify the invocation of the jdeploy GitHub action to use the target_repository parameter, and change the github_token parameter to use your personal access token instead of the default github_token.

- name: Publish with jDeploy
  uses: shannah/jdeploy@master
  with:
    github_token: '${{ secrets.YOUR_PERSONAL_ACCESS_TOKEN }}'
    target_repository: 'yourname/yourapp-releases'

The next time your run the workflow, it should publish the releases to the yourapp-releases repository instead of the yourapp repository.

File Description

icon.png

The icon for your app

installsplash.png

The splash screen to display in the installer.

package-info.json

A JSON file including all of the package information about your app.

The "jdeploy" tag

This action will add (and update) a tag named "jdeploy" to your repo, with a file named "package-info.json". This tag must not be deleted. The package-info.json file is used by both the jDeploy launcher and installer obtain details about your app, so that it knows what versions are available.

The jDeploy JavaFX Starter Project Template

If you’re starting out with a new JavaFX project, then the jdeploy-javafx-starter may be what you’re looking for. It provides you with a bare-bones JavaFX maven project, based on the official openjfx javafx-archetype-simple archtype, but it comes loaded with a GitHub workflow to auto-generate native installers for all branches and releases.

Getting started is as simple as pressing "Use this template", and you’ll be all set.

Appendix: package.json Reference

jDeploy uses the package.json file to store all of its configuration details for a project. When you run jdeploy init it will create a default package.json file in the current working directory with default settings.

Note
For full documentation on the package.json file and its standard properties, see the npm package.json documentation.

Most of the jDeploy-related settings are located inside the "jdeploy" object. E.g.

{
    ...
    "jdeploy" : {
        "jar" : "dist/my-app.jar"
        ...
    }
}

However, there are a few properties at the "root" level that will affect how jDeploy deploys your application.

Main Root-Level Properties

The following standard properties are important to jDeploy.

Property Description Required Default

name

The name used to install your app from NPM. This must be unique in the NPM registry. E.g. If your "name" is

Yes

bin

An map containing the binaries for your application. This must contain at least one entry whose key is the name of your app as it will be called on the command-line, and value is "jdeploy-bundle/jdeploy.js"

Yes

dependencies

Must include a dependency on the "shelljs" library. "dependencies": {"shelljs": "^0.8.4"}

Yes

For other root-level properties see npm package.json documentation

Basic jDeploy Object Properties

The following properties should be placed inside the "jdeploy" object of the package.json, and are almost always used.

Property Description Type Required Default

javaVersion

The Major java version to use for your app’s runtime. Accepted values: "8", "11", "17".

String

No

"11"

jdk

Whether the app requires the full JDK to run. true or false

boolean

No

false

javafx

Whether the app requires JavaFX to run. true or false

boolean

No

false

javafxVersion

Optional JavaFX version to use. If you don’t specify this, then it will just use whatever version comes with the ZuluFX Java distribution.

String

No

None

jar

Relative path to the executable jar file that contains your app. You should supply either "war" or "jar", but not both. This value may be the path to a file, or a glob pattern (e.g. using * and ?) that can be used to match a file.

String

No

title

The human-readable title of your app. This is used in the download page, and in the app and installer names.

String

No

A modified version of the package name.

war

Relative path to a war file, or web-app directory that contains your web app. You should supply either "war" or "jar", but not both. This value may be the path to a file, or a glob pattern (e.g. using * and ?) that can be used to match a file.

String

No

Advanced jDeploy Object Properties

The following properties may be placed in the "jdeploy" object of the package.json file.

Property Description Type Required Default

args

List of runtime arguments. See Runtime Arguments.

Array<String>

No

None

bundles

List of the platforms to build bundles for when you run jdeploy package. E.g. ["mac-x64", "mac-arm64", "win", "linux"]. Bundles will be built in the jdeploy/bundles directory. Not related to the bundles available on the download page. See Building Bundles Locally.

Array

No

None

files

List of copy rules for copying files into the the deployed application bundle. By default, for executable jar deployments, it will copy the jar file and all files listed in the jar’s class path (from its manifest file); and for war deployments, it will just copy what is inside the war file. If you need additional files to be deployed (e.g. native libs or resources), then you should specify them in this list. See Copy Rules.

Array

No

documentTypes

Array of file association objects. See File Associations.

Array

No

urlSchemes

Array of URL schemes to associate with your app. See URL Schemes.

Array

No

installers

List of the platforms to build installers for when you run jdeploy package. E.g. ["mac-x64", "mac-arm64, "win", "linux"]. Installers will be built in the jdeploy/installers directory. Not related to the bundles available on the download page. See Building Bundles Locally.

Array

No

None

jdeployVersion

Optionally specify the version of jDeploy that should be used for building bundles and installers.

String

No

None

port

For war deployments, this specifies the port that the Jetty wrapper should bind to. This can be overridden at runtime using the -Djdeploy.port or -Dport command-line flags, or via the PORT environment variable. If this option is omitted, and no port is specified at runtime, it will use "0" as the port, which will cause it to bind to a random open port.

int

No

0

antFile

Set the ant build file to use for the preCopyTarget and postCopyTarget options.

String

No

build.xml

preCopyTarget

You can optionally specify ANT to run a target before copying files to the jdeploy-bundle directory. You can specify the ant build script that contains the target using the antFile property.

String

No

postCopyTarget

Optional ANT target to be run after files have been copied to the jdeploy-bundle directory. You can specify the ant build script that contains the target using the antFile property.

String

No

preCopyScript

Optional script to be executed before copying files to the jdeploy-bundle directory.

String

No

postCopyScript

Optional script to be executed after copying files to the jdeploy-bundle directory.

String

No

Copy Rules

The "jDeploy/files" array includes zero or more copy rules. Each copy rule is an object that may contain the following properties:

Property Description Type Required Default

dir

The relative path to the directory from which files are to be copied.

String

Yes

includes

Specifies files to be included in the copy. Either an array of glob patterns, or a comma-delimited string of glob patterns. The glob pattern uses PathMatcher globs. E.g. "*/.dll".

String or Array

No

All files in dir

excludes

Specifies files to be excluded from the copy. Either an array of glob patterns, or a comma-delimited string of glob patterns. The glob pattern uses PathMatcher globs.

String or Array

No

None

Example Copy Rules*

{
  ...
  "jdeploy" : {
    "files" : [
        {"dir" : "dist", "includes": "lib/*.dll,lib/*.so,lib/*.dylib"}
    ]
  }
  ...
}

This would copy all of the dll, so, and dylib files from "dist/lib" into the jdeploy-bundle directory for deployment.

Automatic jar/war file Resolution

The "jDeploy/jar" and "jDeploy/war" properties specify the location of the jar or war file that constitutes the application. jdeploy init will scan the current directory for eligible jar, war, and web app directories and set the shallowest candidate in the "jDeploy/jar" or "jDeploy/war" directive of the package.json. If you delete this value, or you omit it from the package.json, jdeploy install and jdeploy publish will perform the same search for an appropriate candidate.

Examples

{
  "bin": {"hellojdeploy": "jdeploy-bundle/jdeploy.js"}, // (1)
  "preferGlobal": true, (2)
  "version": "1.0.1",
  "jdeploy": {"jar": "dist/HelloJDeploy.jar"}, (3)
  "dependencies": {"shelljs": "^0.8.4"},
  "license": "ISC",
  "name": "hello-jdeploy", (4)
  "files": ["jdeploy-bundle"] (5)
}
  1. Once installed, the binary name of our app will be "hellojdeploy". I.e. we would start the app by typing "hellojdeploy" at the command prompt.

  2. Indicates that NPM should prefer to install the app globally. If people forget to include the "-g" flag when they try to install the app, they will get a warning.

  3. The executable Jar file that we are deploying is located at "dist/HelloJDeploy.jar".

  4. The command to install the app would be npm install -g jdeploy-bundle

The above package.json deploys an app such that it would be installed with

npm install -g hello-jdeploy

And it would be installed as "hellojdeploy", so that it could be run on the command line:

hellojdeploy

Appendix: Building Executable Jar File

Tip
The jdeploy-javafx-starter template comes preloaded with everything necessary to distribute your app as native installer. If you’re starting fresh, we recommend you start with that template. The following section describes how to modify existing maven projects to produce an executable Jar file.

jDeploy essentially converts an executable Jar file into native bundles for Mac, Windows, and Linux. An Executable Jar file is essentially just a Jar file that can be run via java -jar TheJar.jar. The distinguishing feature of an executable jar file (vs a regular jar file) is that it includes directives in its manifest that specify the "Main" class to run, and a "Class-Path" (i.e. a list of dependencies). Most IDEs and build tools have built-in support for generating executable Jar files. In some cases, however, you may need to explicitly enable this.

Executable Jar Files in Maven

The two most common strategies to produce executable jars in Maven are using either a combination of the maven-dependency-plugin and the maven-jar-plugin, or using the maven-assembly-plugin. The latter approach (maven-assembly-plugin) can generate a single "fat" jar with all of your dependencies. The former approach (maven-dependency-plugin and maven-jar-plugin) places your dependencies in a "libs" directory.

Appendix: The jdeploy-javafx-archetype

The jdeploy-javafx-starter Github template, which provides you with a JavaFX app project and native installers out of the box, is essentially the same as the jdeploy-javafx-archetype maven archetype.

If you like to use Maven archetypes to generate your new project, you may prefer to use the archetype directly.

I generally prefer the former approach because sometimes bundling everything into a fat jar can break things. Also, when using jDeploy, there is no benefit to a fat jar, since your app is deployed as a native bundle anyways.

Using the maven-dependency-plugin and maven-jar-plugin

In Maven, for example, you will need to use the the maven-dependency-plugin and maven-jar-plugin plugins to package up all the dependencies, and add the appropriate manifest entries to make the jar executable. The following snippet can be added to the <build>/<plugins> section of your pom.xml file.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>
                    ${project.build.directory}/libs
                </outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>libs/</classpathPrefix>
                <mainClass>
                    com.example.YourMainClass
                </mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

You will need to change the <mainClass>com.example.YourMainClass</mainClass> entry to point to the main class of your app.

Using the maven-assembly-plugin

The maven-assembly-plugin is an alternate way to produce an executable jar. It will generate a "fat" jar with all dependencies.

Note
With jDeploy there is no benefit to "fat" jar because your app will be bundled as a native bundle anyways.

The following snippet can be added to the <build>/<plugins> section of your pom.xml file.

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.4</version>
    <executions>
        <execution>
            <id>build-publisher</id>
            <configuration>
                <appendAssemblyId>false</appendAssemblyId>
                <archive>
                    <manifest>
                        <mainClass>com.example.YourMainClass</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <finalName>${project.artifactId}</finalName>
            </configuration>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>

    </executions>
</plugin>

You will need to change the <mainClass>com.example.YourMainClass</mainClass> entry to point to the main class of your app.

Appendix: Windows Installation Layout

The jDeploy application installer creates a slightly different file structure for the installed application on each platform. Additionally, the layout will depend on which version of Java your application specifies in the javaVersion property of the package.json file. Finally, the layout for apps deployed via npm will differ slightly from apps that are deployed via GitHub releases.

Windows Installation Layout for Apps Deployed via npm

Given an application with package name "myapp" and title "My App", the following file structure will be created on Windows:

If the app uses Java 8:

Location Description

%HOMEPATH%/.jdeploy/apps/myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/myapp/bin/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/apps/myapp/jre

The Java runtime environment that is bundled with the app.

%HOMEPATH%/.jdeploy/uninstallers/myapp/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/packages/myapp

This diectory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

If the app uses Java 11 or higher:

Location Description

%HOMEPATH%/.jdeploy/apps/myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/myapp/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/uninstallers/myapp/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/packages/myapp

This diectory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

Windows Installation Layout for Apps Deployed via GitHub Releases

The layout for apps deployed via GitHub releases is slightly different. The main difference is that the app is installed in a directory that is named after the hash of the app’s package name and version. This is to ensure that multiple versions of the same app can be installed side-by-side. Each app has a unique hash prefix that is derived from the package name and version of the app. This hash prefix is used to create a unique directory for the app. In addition, the package is installed in the %HOMEPATH%/.jdeploy/gh-packages directory, rather than the %HOMEPATH%/.jdeploy/packages directory.

If the app uses Java 8:

Location Description

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp/bin/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp/jre

The Java runtime environment that is bundled with the app.

%HOMEPATH%/.jdeploy/uninstallers/<APP_HASH_PREFIX>.myapp.<BRANCH_NAME>/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/gh-packages/<APP_HASH_PREFIX>.myapp

This diectory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/github.com/<GITHUB_USER>/<GITHUB_REPO>/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

If the app uses Java 11 or higher:

Location Description

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp

This is the root directory for the application. It contains the following subdirectories:

%HOMEPATH%/.jdeploy/apps/<APP_HASH_PREFIX>.myapp/My App.exe

The executable file for the application. This is the file that will be run when the user clicks on the app icon.

%HOMEPATH%/.jdeploy/uninstallers/<APP_HASH_PREFIX>.myapp/myapp-uninstall.exe

The uninstaller for the app. This installer will be run when you uninstall the application via Add/Remove Programs.

IMPORTANT: You should not run the installer directly. Always run it via Add/Remove Programs.

%HOMEPATH%/.jdeploy/gh-packages/<APP_HASH_PREFIX>.myapp

This diectory contains all of your actual Java application files. This is where your jar file, war file, or web app directory will be installed.

%HOMEPATH%/.jdeploy/log/github.com/<GITHUB_USER>/<GITHUB_REPO>/myapp/myapp.lastrun.log

The log file for the last run of the application. If you write to StdOut or StdErr in your application, this is where the output will be captured.

Why is the layout different on Java 8

Apps that use Java 8 on Windows use a slightly different file structure than other versions of Java because OpenJDK 8’s support for a dedicated Java Runtime Environment (JRE) was very specific about the app structure. On Java 11 and higher, it will use the JRE specified by the JAVA_HOME environment, so the jDeploy launcher simply needs to set this value appropriately before starting the JVM. On Java 8, however, it ignores the JAVA_HOME setting when it comes to the JRE, and instead tries to find the location of the global JRE in the Windows registry.

Using a global JRE is not ideal for apps that are distributed as standalone executables, because it can lead to conflicts with other Java apps on the system, so fortunately, Java 8 provided one other option.

If the app exe resides in a folder named "bin", then the Java launcher will look for a JRE in a directory named "jre" in the same directory as the "bin" directory. E.g. If the app.exe is located at /path/to/bin/app.exe, then it will look for a JRE at /path/to/jre. In order to satisfy this requirement, and not disrupt the majority of apps, we decided to treat Java 8 specially.

We create a "jre" directory in the app’s directory, and copy the JRE into that directory, and move the app’s exe file into a "bin" directory.