I have written a Java 17 application that provides several MBeans. If I start the jar with a standard Java 17 SDK via java -jar myjar.jar the applications starts and I can connect to it from JConsole.
Then I have created a custom runtime image with jlink.
The application itself runs fine with this image. It gets the MBeanServer and registers successfully my MBean.
When I start Jconsole I can select the running application (as "Local Process") but then the connection to this process can't be created.
I have tried to add a number of additional modules (like jdk.management, java.management, ...) but without success. It seems that at least one module is missing because in my customer runtime image e.g. the DLL management_ext.dll is missng.
Any idea which modules are necessary?
Try use jdeps to check all modules are required in your build.
Example of listing all modules from .jar
jdeps --ignore-missing-deps --print-module-deps --multi-release 17 your-jar.jar
Example to jlink
jlink \
--module-path /path-with-all-jdk-modules \
--add-modules $(jdeps --ignore-missing-deps --print-module-deps --multi-release 17 your-jar.jar ) \
--output /path-output-jre-custom \
--strip-debug \
--no-header-files \
--no-man-pages \
--compress 2
Related
For a while now I've been working with modular projects, but due to being constrained with filename and automatic modules, I had never got a chance to work with jlink tool to produce a redistributable application image. Today I've opted to start an independent project which does not import any external dependencies to prevent usage of the compatibility mode. The project consists of 3 modules and is in maven, so I will only be posting the jlink command snippet I'm using.
Project for reference: https://gitlab.com/Dragas/edu-day-demo, checkout the modules-full tag. Project is built with package goal, to prevent polluting your local .m2 repository. Project is already configured to pull dependencies so packaging and deployment would be easier.
The command I used to generate the jlinked image was as follows:
jlink \
--module-path edu-day-runtime/target/dependency/:edu-day-runtime/target/ \
--add-modules ALL-MODULE-PATH \
--output edu-day-jlinked \
--launcher edurun=edu.day.runtime
Invoking the command does indeed generate a jlinked image, which contains minimum required modules, java libraries and JVM binaries to run the project. Invoking the machine that built the image
edu-day-jlinked/bin/edurun 1 1
does run the project and outputs the following
Result of sum is 2
Meanwhile, attempting to run the same in containerized environment (here i'm using bash:5, a non-java image to simulate an environment where java is not installed) does not yield similar results. Instead, the shell does not seem to find a binary named java
docker run -it -v "$(pwd)/edu-day-jlinked:/app" bash:5
...(in container)
bash-5.0# /app/bin/edurun 1 1
/app/bin/edurun: line 4: /app/bin/java: not found
Upon inspection, the folder does indeed contain the binary called java
bash-5.0# ls -la
total 52
drwxr-xr-x 2 1000 1000 4096 Aug 23 07:53 .
drwxr-xr-x 7 1000 1000 4096 Aug 23 07:53 ..
-rwxr-xr-x 1 1000 1000 116 Aug 23 07:53 edurun
-rwxr-xr-x 1 1000 1000 16688 Aug 23 07:53 java
-rwxr-xr-x 1 1000 1000 16712 Aug 23 07:53 keytool
But even invoking it directly (to show the help message) does not yield any results, besides the same message that the binary cannot be found
(in /app/bin/ folder)
bash-5.0# ./java
bash: ./java: No such file or directory
What is more interesting is that even the keytool binary returns the same error
(in /app/bin/ folder)
bash-5.0# ./keytool
bash: ./keytool: No such file or directory
This raises a question: what went wrong? I haven't yet delved deeper into how jlink works, but my speculation is that it copies the binaries from my own java installation (openjdk 11.0.8+10 from arch repositories), and considers that to be redistributable. Or did I just miss some command line options?
Your issue is that the test container (bash:5) doesn't use the same version of the run-time linker as the java environment.
The binary produced by the jlink will only run if there is a compatible linux run-time linker on the system.
The purpose of the run-time linker is to configure the binary for execution on the system - at the time you are building an executable the default run-time linker is hard-coded into the binary. You can inspect the run-time linker using a tool such as readelf -l, or ldd (ldd only works if it can find the run-time linker)
The default run-time linker for amd64 linux (e.g. ubuntu) is: /lib64/ld-linux-x86-64.so.2
The default run-time linker for i386 linux is: /lib/ld-linux.so.2
On a bash:5 container, the default run-time linker is: /lib/ld-musl-x86_64.so.1
This is not compatible with the run-time linker for the jdk
The error: /app/bin/java: not found is caused because the run-time linker cannot be found for the binary. A dirty test of a jlinked VM in a bash:5 container gives the same error.
When I get the run-time linker for the java I've used:
$ docker run --rm -it -v (pwd)/edu-day-jlinked64:/app -w /here bash:5 bash
bash-5.0# /app/bin/java
bash: /app/bin/java: No such file or directory
bash-5.0# strings -a /app/bin/java | grep '^/lib'
/lib64/ld-linux-x86-64.so.2
bash-5.0# ls -l /lib64/ld-linux-x86-64.so.2
ls: /lib64/ld-linux-x86-64.so.2: No such file or directory
Testing with the run-time linker that's on-board:
bash-5.0# /lib/ld-musl-x86_64.so.1 --list /app/bin/java
/lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
libjli.so => /app/bin/../lib/libjli.so (0x7fe28528c000)
libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
libz.so.1 => /lib/libz.so.1 (0x7fe285272000)
libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7fe2852a3000)
Error relocating /app/bin/../lib/libjli.so: __snprintf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __vfprintf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __read_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __memmove_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __printf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __fprintf_chk: symbol not found
Error relocating /app/bin/../lib/libjli.so: __sprintf_chk: symbol not found
so it definitely won't work here.
Let's use something 'standard'. As I had built the jlinked app in an ubuntu:focal container, with an installed version of java let's use one that doesn't have java built-in:
$ docker run --rm -it -v $(pwd)/edu-day-jlinked64:/app -w /here ubuntu:focal bash
root#865c9c12c029:/here# /app/bin/java
Usage: java [options] <mainclass> [args...]
(to execute a class)
or java [options] -jar <jarfile> [args...]
(to execute a jar file)
or java [options] -m <module>[/<mainclass>] [args...]
java [options] --module <module>[/<mainclass>] [args...]
(to execute the main class in a module)
or java [options] <sourcefile> [args]
(to execute a single source-file program)
so it will work in this case.
Reproducibility:
Built using:
$ docker run --rm -it -v $(pwd):/here -w /here ubuntu:focal bash
# apt-get update
# DEBIAN_FRONTEND=noninteractive apt-get install -y git openjdk-14-jdk maven
# git clone https://gitlab.com/Dragas/edu-day-demo .
# git checkout modules-full
# ./mvnw package
# rm -rf edu-day-runtime/target/classes
# jlink --module-path edu-day-runtime/target/dependency/:edu-day-runtime/target/ --add-modules ALL-MODULE-PATH --output edu-day-jlinked --launcher edurun=edu.day.runtime
# ./edu-day-jlinked/bin/edurun 1 1
Result of sum is 2
In an adjacent directory:
$ docker run --rm -it -v $(pwd)/edu-day-jlinked:/app -w /here bash:5 bash
bash-5.0# /app/bin/edurun 1 1
/app/bin/edurun: line 4: /app/bin/java: not found
In another directory:
$ docker run --rm -it -v $(pwd)/edu-day-jlinked:/app -w /here ubuntu:focal bash
root#200b4a98f9ee:/here# /app/bin/edurun 1 1
Result of sum is 2
TL;DR — The bash:5 image uses a C library that is binary incompatible with the C library that was linked with your edu-day-jlinked/bin/java executable.
The long version
„…This raises a question: what went wrong?…“
What's going wrong is your app/bin/java binary is failing to find the C library that it was originally linked to when you built your edu-day-jlinked executable on whatever machine you built it on locally.
The problem occurs because the java binary that jlink produced is linked to the GNU glibc library that your locally-installed JDK uses…
$ ldd edu-day-demo-modules-full/edu-day-jlinked/bin/java
…
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa61a95b000)
…
Whereas, the bash:5 image runs in the Busybox Linux distribution. And Busybox does not use glibc…
bash-5.0# ldd app/bin/java
…
libjli.so => app/bin/../lib/jli/libjli.so (0x7f572a16d000)
…
libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f572a19f000)
Error relocating app/bin/../lib/jli/libjli.so: __snprintf_chk: symbol not found
Error relocating app/bin/../lib/jli/libjli.so: __vfprintf_chk: symbol not found
Error relocating app/bin/../lib/jli/libjli.so: __read_chk: symbol not found
Error relocating app/bin/../lib/jli/libjli.so: __memmove_chk: symbol not found
Error relocating app/bin/../lib/jli/libjli.so: __printf_chk: symbol not found
Error relocating app/bin/../lib/jli/libjli.so: __fprintf_chk: symbol not found
Error relocating app/bin/../lib/jli/libjli.so: __sprintf_chk: symbol not found
It uses a different C library: musl…
bash-5.0# find / -name '*musl*'
/lib/libc.musl-x86_64.so.1
/lib/ld-musl-x86_64.so.1
It helps to understand the Linking process. And it also helps to bear in mind that JLink builds a custom executable for a specific environment.
Your trial run on your local machine worked, because jlink built the executable specifically for your local environment.
The proposed solution
„…docker here is intended to simulate an environment which does not have java installed…“
Here is a Dockerfile that successfully builds your application and the resulting image „does not have java installed“…
FROM maven:3.6.1-jdk-13-alpine as build
WORKDIR /app
COPY pom.xml .
COPY edu-day-sum edu-day-sum
COPY edu-day-runtime edu-day-runtime
COPY edu-day-api edu-day-api
RUN mvn package && \
--module-path ${JAVA_HOME}/jmods:edu-day-runtime/target/dependency/:edu-day-runtime/target/edu-day-runtime-1.0-SNAPSHOT.jar \
--add-modules ALL-MODULE-PATH \
--output edu-day-jlinked \
--launcher edurun=edu.day.runtime
FROM alpine:latest
COPY --from=build /app/edu-day-jlinked /app
ENTRYPOINT ["/app/bin/edurun"]
CMD ["1", "1"]
Docker best practice advises: „Use multi-stage builds“ (like in the above Dockerfile) when your aim is to build „an environment which does not have java installed“.
The FROM maven:3.6.1-jdk-13-alpine stage of the multi-stage build, uses an alpine Linux image that has both Maven and a JDK specifically built to be compatible with the alpine distribution.
The FROM alpine:latest is a very small linux distro that does not have Java on it. The maven:3.6.1-jdk-13-alpine layer is discarded as the Docker best practice docs says. The only java in the resulting image is the one in app/bin.
I use jpackage (with JDK 14) for creating a standalone JavaFX application.
The output of jpackage looks strange, it consists of two the same copies of JRE and all files in app folder, except App.cfg file, can be deleted without any problem.
Does jpackage can do not generate unnecessary files?
What I have:
I have a simple project created from Maven archetype org.openjfx:javafx-archetype-simple:0.0.3.
And I just added maven-shade-plugin for creating jar with dependencies and a simple launching class for it (like NewMain class here).
I do:
mvn package
mvn javafx:jlink
jpackage --package-type app-image --dest targetApp --name App --runtime-image target/image --main-jar testApp-1.0-SNAPSHOT.jar --input target
As result I have two folders with JRE (absolutely the same ones):
targetApp\App\runtime
targetApp\App\app\image
And all files (and folders) (JRE (~53 MB), Jar (~9 MB), classes...) in targetApp\App\app can be safely deleted, except App.cfg file.
After deleting these files I run targetApp\App\App.exe and it works fine.
The deleting reduces the size of App from 118 to 56 MB.
An additional question: I use Windows and jpackage generates the result for Windows (a lot of dll files and exe), do I can create a standalone application in Windows for Mac and Linux?
You get this result because you already created an uber-jar which contains everything in addition to what you specified via --input target which, according to the documentation, has the following effect.
All files in the input directory will be packaged into the
application image.
Try whether it works to just remove the option --input target or don't create the uber-jar. Instead use the maven-dependency-plugin to copy all dependencies into a single folder, say target/libs, and then explicitly specify --input target/libs.
Your second question:
You cannot cross-create applications for other targets, if that is your question.
You might want to check out the JPackageScriptFX project on GitHub. It contains a sample multi-module maven project structure and build scripts for Mac and Windows that will build all available package types and executables for both platforms. You can find it here: https://github.com/dlemmermann/JPackageScriptFX
My solution without Oracle's jpackage: AdoptOpenJDK + jpackager + macOS => .pkg, .dmg
Download AdoptOpenJDK_13. Unzip and put into /Library/Java/JavaVirtualMachines/adoptopenjdk-13.jdk
Download jpackager for (in my case osx) from https://mail.openjdk.java.net/pipermail/openjfx-dev/2018-September/022500.html. Unzip.
Copy jdk.packager.jar and jpackager to the /Library/Java/JavaVirtualMachines/adoptopenjdk-13.jdk/Contents/Home/bin (!!!)
In /yourworkspace/target should be the programA.jar file. (In my case it is a small program created with OpenJFX + openjdk13)
The /yourworkspace/jlink denotes on your JRE with bin,lib etc. you might have created via jlink. (In my case I have my custom JRE + JavaFX modules)
The /yourworkspace/icons contains .icns .
create jpackager.command file in your workspace with the content:
#! /bin/bash
ABSPATH=$(cd "$(dirname "$0")"; pwd -P)
launcher=`ls $ABSPATH/target/*.jar`
for eachfile in $launcher
do
if [ -f "$eachfile" ];then
echo Creating .pkg ...
echo "$eachfile"
basename="${eachfile##*/}"
/Library/Java/JavaVirtualMachines/adoptopenjdk-13.jdk/Contents/Home/bin/jpackager \
create-installer \
--runtime-image $ABSPATH/jlink \
--name ProgramA \
--input $ABSPATH/target \
--main-jar $basename \
--version 1.0 \
--copyright "Nikita Gromov 2020" \
--name "ProgramA-macos" \
--mac-bundle-name "ProgramA" \
--output $ABSPATH/appimage \
--icon $ABSPATH/icons/20200220101822955_easyicon_net_32.icns
fi
done
Double click on jpackager.command which is located in /yourworkspace and wait until the jpackager has created ProgramA-macos-1.0.pkg and ProgramA-macos-1.0.dmg under /yourworkspace/appimage
My solution without Oracle's jpackage: AdoptOpenJDK + jpackager + Windows => .msi
Install WiX (!!!) Important step.
Download AdoptOpenJDK_11. Will be installed under C:\Program Files\AdoptOpenJDK\jdk-11.0.6.10-hotspot
Download jpackager (in this case win) from https://mail.openjdk.java.net/pipermail/openjfx-dev/2018-September/022500.html. Unzip.
Copy jdk.packager.jar and jpackager to the C:\Program Files\AdoptOpenJDK\jdk-11.0.6.10-hotspot\bin (!!!)
In /yourworkspace/target should be the programA.jar file. (In my case it is a small program created with OpenJFX + openjdk11)
The /yourworkspace/jlink denotes on your JRE with bin,lib etc. you might have created via jlink. (In my case I have my custom JRE + JavaFX modules)
The /yourworkspace/icons contains .icns .
create jpackager.bat file in your /yourworkspace folder with the content:
set openjdk=C:\Program Files\AdoptOpenJDK\jdk-11.0.6.10-hotspot\bin
set saveto=%cd%
cd %openjdk%
jpackager create-installer --runtime-image %saveto%/jlink --input %saveto%/target --main-jar ProgramA.jar --version 1.0 --copyright "Nikita Gromov" --name "ProgramA-win" --output %saveto%/appimage --icon %saveto%/icons/icon.ico --win-menu --win-shortcut --win-dir-chooser
Note The ProgramA.jar should be in /yourworkspace/target path (!!!)
Double click on jpackager.bat which is located in /yourworkspace and wait until the jpackager has created ProgramA-win-1.0.msi under /yourworkspace/appimage
Custom JRE + JavaFX mods on Windows
Create /yourworkspace/jlink.bat file. The content of it should be:
SET openjdk=C:\Program Files\AdoptOpenJDK\jdk-11.0.6.10-hotspot\bin
SET fx=C:\Programme\AdoptOpenJDK\javafx-jmods-11.0.2
set saveto=%cd%
cd %openjdk%
jlink --module-path %fx% --add-modules=javafx.base --add-modules=javafx.controls --add-modules=javafx.fxml --add-modules=javafx.graphics --add-modules=javafx.web --add-modules=javafx.media --add-modules=javafx.swing --bind-services --output "%saveto%\jlink"
This creates the /yourworkspace/jlink folder with a custom JRE+JavaFX libraries which you will link to jpackager.
I have created a module com.company.ep that is located in the source folder com.company.ep. (Yes, I have removed src from the build path and deleted it!) Inside the source folder, I have a couple of packages as the following:
com.company.ep <--- root source folder
com.company.ep.main <--- package 1
com.company.ep.model <--- package 2
com.company.ep.view <--- package 3
// ... more packages
module-info.java
The main class is located in the package com.company.ep.main.Main. In my module-info.java, I have configured the dependencies:
module com.company.ep {
exports com.company.ep.main;
exports com.company.ep.model;
exports com.company.ep.view;
// ... more exports
requires javafx.controls;
requires javafx.graphics;
}
When I tried to launch my program, eclipse told me that:
Error occurred during initialization of boot layer
java.lang.module.FindException: Module javafx.controls not found, required by com.company.ep
So, I tried to run it on the command prompt:
java -p d:\Applications\openjfx-sdk-11\lib;bin -m com.company.ep/com.company.ep.main.Main
bin is the output folder of eclipse, and it worked.
So, I went to Properties → Run/Debug Settings → Main → Show Command Line, it showed:
D:\Applications\openjdk-11.0.1\bin\javaw.exe -Dfile.encoding=UTF-8 -p "D:\Development\Eclipse-Workspace\MyProject\bin" -classpath "D:\Applications\openjfx-sdk-11\lib\javafx.base.jar;D:\Applications\openjfx-sdk-11\lib\javafx.controls.jar;D:\Applications\openjfx-sdk-11\lib\javafx.fxml.jar;D:\Applications\openjfx-sdk-11\lib\javafx.graphics.jar;D:\Applications\openjfx-sdk-11\lib\javafx.media.jar;D:\Applications\openjfx-sdk-11\lib\javafx.swing.jar;D:\Applications\openjfx-sdk-11\lib\javafx.web.jar;D:\Applications\openjfx-sdk-11\lib\javafx-swt.jar" -m com.company.ep/com.company.ep.main.Main
I have created a user library with all JARs added, and the library is added to the project's Modulepath.
Then I have tried to set the module path explicitly in VM arguments in Run/Debug Settings: -p D:\Applications\openjfx-sdk-11\lib, I'd still no luck.
My questions are:
Why javaw.exe?
Why classpath? As my library is added as a module-path entry.
How to configure the module dependencies in eclipse.
I am not sure if I have configured eclipse correctly, or whether it is probably a problem of OpenJDK as it worked when I worked on another computer with Oracle Java SE installed.
Thank you!
The explanation of why Eclipse fails on running your modular project can be found in the OpenJFX docs for Eclipse (modular from IDE section).
As it was already mentioned:
Being a modular project, and since we already added the JavaFX SDK library to the module-path, there is no need to add any VM arguments.
But if you run on Eclipse you will get the mentioned error:
Error occurred during initialization of boot layer
java.lang.module.FindException: Module javafx.graphics not found, required by hellofx
So why is it failing??
As explained in the docs:
This exception happens because the Eclipse ant task overrides the module-path
How does this happen??
Checking the command line applied (Show Command Line from Run Configurations...), you can find out why:
$JAVA_HOME/bin/java -Dfile.encoding=UTF-8 \
-p bin/hellofx \
-classpath $PATH_TO_FX \
-m hellofx/org.openjfx.MainApp
If you copy it and paste it and run it in a terminal, it will fail of course with the same message. The reason is that Eclipse doesn't add the JavaFX library to the module path.
If the task generates the wrong arguments, let's try to fix it by adding our own VM arguments by editing Run configurations... and adding -p $PATH_TO_FX:bin/hellofx.
But if you run it, it will fail again.
Let's check why, with Show Command Line from Run Configurations...
$JAVA_HOME/bin/java -Dfile.encoding=UTF-8 \
-p $PATH_TO_FX:bin/hellofx \
-p bin/hellofx \
-classpath $PATH_TO_FX \
-m hellofx/org.openjfx.MainApp
As you can see, the user's VM arguments are added before the default ant task arguments, so there are two -p (--module-path) options, and the first one (the user's one with the JavaFX jars) is overridden by the second one (only the project's module), so, again, the JavaFX jars are not added to the module path, and hence you get the error.
So how can we fix it??
As mentioned in the linked documentation, the possible fix is:
To prevent this issue click on Run -> Run Configurations... -> Java Application -> Dependencies, select Override Dependencies... and add -p /path-to/javafx-sdk-11/lib:bin/hellofx, and press Override.
With this solution, you can see it works, and you can check the command line:
$JAVA_HOME/bin/java -Dfile.encoding=UTF-8 \
-p $PATH_TO_FX:bin/hellofx \
-p bin/hellofx \
-classpath $PATH_TO_FX \
-p /path-to/javafx-sdk-11/lib:bin/hellofx \
-m hellofx/org.openjfx.MainApp
Basically we are adding again the "right" module path option, after all the failed ones.
While now the project runs, the solution is obviously not nice.
Here you can find a sample referred from the OpenJFX documentation.
EDIT
Based on #kleopatra comments, another workaround to make it work is the following:
For some reason, the library JavaFX11 (that contains modular jars) is not scanned and Eclipse doesn't include those jars into its -p option, but into the classpath:
$JAVA_HOME/bin/java -Dfile.encoding=UTF-8 \
-p bin/hellofx \
-classpath $PATH_TO_FX \
...
But, if you add those jars directly to the module path, it will do add them, and this will run fine:
$JAVA_HOME/bin/java -Dfile.encoding=UTF-8 \
-p bin/hellofx:$PATH_TO_FX/javafx.base.jar:...:$PATH_TO_FX/javafx.controls \
...
Then with this there is no more need to override the dependencies.
EDIT 2
As #mipa points out in a comment, there was a bug filed on this issue, and it has already been solved. I've tested it with Eclipse 2018-12 M2 (4.10.0M2) Build id: 20181108-1653, and it works with the JavaFX11 library only (as it should):
$JAVA_HOME/bin/java -Dfile.encoding=UTF-8 \
-p bin/hellofx:$PATH_TO_FX/javafx.base.jar:... \
-m hellofx/org.openjfx.MainApp
I created runtime image using jlink on my Linux machine. And I see linux folder under the include folder. Does it mean that I can use this runtime image only for Linux platform? If yes, are there any ways to create runtime images on one platform for another (e.g. on Linux for Windows and vice versa)
The include directory is for header files, such as jni.h, that are needed when compiling C/C++ code that uses JNI and other native interfaces. It's nothing to do with jlink.
The jlink tool can create a run-time image for another platform (cross targeting). You need to download two JDKs to do this. One for the platform where you run jlink, the other for the target platform. Run jlink with --module-path $TARGET/jmods where $TARGET is the directory where you've unzipped the JDK for the target platform.
Being generally unable to add anything to Alan Bateman's answers in terms of information, I'll offer a working example. This example illustrates using jlink on Mac OS and then running the binary on Ubuntu in a Docker container.
The salient points are as follows.
Given two simple modules, we compile on Mac OS:
javac -d build/modules \
--module-source-path src \
`find src -name "*.java"`
jar --create --file=lib/net.codetojoy.db#1.0.jar \
-C build/modules/net.codetojoy.db .
jar --create --file=lib/net.codetojoy.service#1.0.jar \
-C build/modules/net.codetojoy.service .
Assuming that the Linux 64 JDK is unpacked in a local directory (specified as command-line arg), we call jlink (on Mac OS in this example). JAVA_HOME is the crux of the solution:
# $1 is ./jdk9_linux_64/jdk-9.0.1
JAVA_HOME=$1
rm -rf serviceapp
jlink --module-path $JAVA_HOME/jmods:build/modules \
--add-modules net.codetojoy.service \
--output serviceapp
Then, assuming we've pulled the ubuntu image for Docker, we can execute the following in a Docker terminal (i.e. Linux):
docker run --rm -v $(pwd):/data ubuntu /data/serviceapp/bin/java net.codetojoy.service.impl.UserServiceImpl
TRACER : hello from UserServiceImpl
To re-iterate this feature of Java 9/jlink: Linux does not have Java installed and the Linux binary was built on Mac OS.
Is there a way to convert a Java application into a Mac OS X executable app?
I use NetBeans to develop in Java, and I'd like to "pack" the "dist" folder into an app (just for convenience)
Use the Apple Java Extensions and its Guide
The Apple Java Extensions contains a very complete development guide with information on the deployment of Java applications on Mac OS X and the production of application bundles. It also introduces other aspects of the Apple Java Extensions, like the support for integration with the standard Mac OS X UI.
Other references:
JarBundler.
Make a Mac OS X App Bundle
Creating a osx app bundle in Java registered to a protocol url
There is a library that let's you package your Java app
Packr: https://github.com/libgdx/packr
Packages your JAR, assets and a JVM for distribution on Windows (ZIP), Linux (ZIP) and Mac OS X (.app), adding a native executable file to make it appear like the app is a native app.
It can even minimize the JRE for you.
jar2app
Packr is a great tool, but at the time I found that I wanted something "easier to use", so jar2app was born. I know this is an old question but perhaps other people might find this program easier to use than other alternatives. If they don't, there's a direct reference in the FAQ to other alternatives (such as Packr).
You can use javapackager tool to build the application and wrap it in into an installer, the following commands show how to convert a jar file into a bundle file:
commands
mkdir -p package/macosx
cp Test.icns package/macosx
jdk=$(/usr/libexec/java_home)
$jdk/bin/javapackager -deploy -native dmg \
-srcfiles Test.jar -appclass package.Test -name Test \
-outdir deploy -outfile Test -v
cp deploy/bundles/Test-1.0.dmg installer.dmg
ls -l
open installer.dmg
To change the application icon and more info MacJava.
As of JDK14 there is also 📦 jpackage (JEP-392), currently promoted from incubation phase to a production-ready feature.
Example usage that worked with an swt app (assumes all the required jar files reside in the files folder; you can also provide a custom resource folder with --resource-dir):
jpackage --type dmg \
-i files \
-n Bigly \
--main-class com.biglybt.ui.Main \
--main-jar BiglyBT.jar \
--java-options -XstartOnFirstThread \
--mac-package-name BiglyBt \
--icon app.icns \
--verbose
For the full option list use:
jpackage --help
WARNING: It's probably debatable whether it's a bug or not, and maybe it will be addressed in a future release, but in the current version, if you specify the input folder as -i ., and do not provide a custom destination with --dest then jpackage will bundle everything in the current folder... including the bundle it just created, i.e. it goes recursive on itself when . is both the input folder and the output :D .
Since some of the links in the accepted answer are no longer available or suitable in 2020, 8 years from the question was asked, I would like to share my findings that I confirmed working today.
There is a tool, javapackager, shipped with java, can package java application on Windows, Linux, macOS for you.
Here is the official manual: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html
(Also there are other helpful tools here: https://docs.oracle.com/javase/8/docs/technotes/tools/ )
For packaging a mac application, I used this command:
javapackager \
-deploy \
-native image \
-srcdir ./csv-encrypt-tool-mac \
-srcfiles csv-encrypt-tool.jar \
-srcfiles dict \
-srcfiles config \
-srcfiles log \
-outdir ./dist \
-outfile csv-encrypt-tool \
-appclass some.package.CsvEncToolApp \
-name "csv-encrypt-tool" \
-title "csv-encrypt-tool" \
-nosign \
-v \
-BjvmOptions=-Xmx4096m \
-BmainJar=csv-encrypt-tool.jar \
-Bicon=icon.icns
And this is the explanation:
-deploy \ # Assembles the application package for redistribution with sys JRE
-native image \ # Build a .app file. If you want a .dmg, use -native dmg
-srcdir ./csv-encrypt-tool-mac \ # directory where my jar and resource files in
-srcfiles csv-encrypt-tool.jar \ # my executable jar, path relative to -srcdir
-srcfiles dict \ # one of my resource directories, path relative to -srcdir
-srcfiles config \ # another one of my resource directories, path relative to -srcdir
-srcfiles log \ # again, one of my resource directories, path relative to -srcdir
-outdir ./dist \ # where I want the package to be put
-outfile csv-encrypt-tool \ # the output file name without extension, the final file (or bundle which is a directory actually) will be csv-encrypt-tool.app
-appclass some.package.CsvEncToolApp \ # the class with main method, which is the entry point of the whole app
-name "csv-encrypt-tool" \ # the name (not very sure what this is for)
-title "csv-encrypt-tool" \ # the title (not sure what it is either)
-nosign \ # not sign the app since this is just an internal tool, if you want to publish it, signing is necessary
-v \ # verbose
-BjvmOptions=-Xmx4096m \ # I need 4GB max heap size
-BmainJar=csv-encrypt-tool.jar \ # the jar file with main class
-Bicon=icon.icns # the icon
After the command is executed, there will be some files and directories created in dist, where I want the package be, and one of the directories is bundles. The application is put in there.
Since I just built an internal tool, there is no need to sign and packaged without other production ready options. You can refer to the official manual for help.
Hope this help others who do not know how to package an application on macOS in 2020, just like me.
So none of these options worked for me (maybe because I am running OS X 10.15, maybe because most of these projects are years old, who knows). Installing Catalina made the existing app I had built around the Java app no longer work.
Ultimately, this post helped: Just use Automator to run a script that runs the java command to launch the jar. I wanted to make an app to launch Colossus, a Java version of the old Avalon Hill board game Titan. I wrote a shell script that looks like:java -Xmx256m -jar /my/path/to/the/game/Colossus.jar net.sf.colossus.appmain.Start and then created an automator application whose only action was "Run Shell Script" that launches that script. Works like a charm, no installing Ant, no command line apps requiring you to download java vms, and best of all it uses an Apple tool so will work with newer versions of OS X.
You can try this app , it bundles your jar file into Mac app
Edit: it's easy to use , select the Jar file and an Icon. Here you can see the screen shoot.
https://github.com/aprsn/Mac-App-Creator