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.
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
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").
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:
In this example, clicking on the "Open SwingSet2", will open the app as shown below:
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.
TipWe 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. ImportantNodeJS 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:
Press Yes.
This will open the jDeploy GUI, and it will look something like the following screenshot.
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.
If it is successful, you’ll see a "Success Message" like the following:
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.
ImportantThis 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.
NoteIf 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.
NotejDeploy 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.
To add a new document type, press the button. To remove a document type, press the 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.
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.
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:
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.
...
<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.
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.
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:
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.
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:
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:
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:
-
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. -
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 thenpx
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.
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:
-
Only Swing/AWT UIs are supported currently. No JavaFX.
-
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. |
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:
-
GitHub actions integration to automate deployments on push/release/tag.
-
Publish app artifacts in the GitHub release itself.
-
Branch-App Synchronization: Distribute app bundles that are automatically synchronized with a specific branch of your repo.
-
Multiple Streams: Distributed multiple versions of your app (e.g. "dev", "stage", "production") that are synchronized to different branches of your repository.
-
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" |
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.
See https://github.com/shannah/jdeploy-javafx-starter for details.
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. |
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. |
boolean |
No |
|
javafx |
Whether the app requires JavaFX to run. |
boolean |
No |
|
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 |
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 |
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 |
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 |
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 |
int |
No |
0 |
antFile |
Set the ant build file to use for the |
String |
No |
build.xml |
preCopyTarget |
You can optionally specify ANT to run a target before copying files to the |
String |
No |
|
postCopyTarget |
Optional ANT target to be run after files have been copied to the |
String |
No |
|
preCopyScript |
Optional script to be executed before copying files to the |
String |
No |
|
postCopyScript |
Optional script to be executed after copying files to the |
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)
}
-
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.
-
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.
-
The executable Jar file that we are deploying is located at "dist/HelloJDeploy.jar".
-
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.
See https://github.com/shannah/jdeploy-javafx-starter-archetype for more information.
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. |