Customizing --module-path for Java - java

I created a javafx application via the IDE IntelliJ IDEA on ArchLinux. Now, I want to start this program without any IDE, so I created a .jar file. When trying to start the program with java -jar myprogram.jar, then I get the error message that runtime components for javafx are missing. Fine - I'll add these with the --module-path option, and it runs.
However, this --module-path thing only works somehow when I set the module path being somewhere in /lib/... On the other hand, when I try to copy the javafx modules into the folder where the myprogram.jar file is and set the module path there, the modules cannot be found.
My issue is the fact that I want to "self-contain" everything possible in one folder, such that I could copy&paste everything on another machine where the JVM is installed and just run it, and do not want to depend on the specific path where the javafx modules are installed. Maybe there is another way than to customize the --module-path?
Any help? Thanks :-)

Forget about trying to create a single executable jar that includes JavaFX components and depends on a pre-installed JVM (it is not a supported configuration).
However, cwellm's answer here details a method that keeps the modular JavaFX dependencies separate from the created application jar by keeping the JavaFX modules in separate jars added to the module path, I believe that this might be a valid and supported deployment configuration. It does require deploying multiple jar files to the client in the right locations in order for it to work. Another alternative would be to assume a distribution of the JDK such as liberica or corretto which can come with JavaFX included in the base JDK distribution, then you don't need to specify a module path for JavaFX as it will already be present in the default boot module path for the JDK.
Recommended Solutions
Instead create a runtime image using jlink, this can be done by (among other methods) using the openjfx maven plugin.
Or if you want an installer, use jpackage which can also (among other methods) be created using maven, see JPackageScriptFX as an example.
jpackage doesn’t create archlinux native packages. So if you want that, then jpackage wouldn’t be the thing to use. Instead you could use a maven assembly or tar call to create a zip or tar.gz of the jlink output, or use the pacman tool to create a native archlinux package of the same.
Or if you like gradle see the badass jlink and runtime plugins.
Using a build tool (maven or gradle) to include the JavaFX dependencies rather than relying a JavaFX sdk download is preferred IMO. Also, if possible, define a module-info. Then jlink can be used to create the required runtime from your app, it’s dependencies and the jre, as well as the execution script for the app, so that things are self-contained. Native packages in rpm or deb format created by jpackage takes this further. Installing apps packaged this way also ensures all required OS lib dependencies are correct.
If you want to understand more see the contextual info (FAQ section) in this answer:
How to create a standalone .exe in Java (that runs without an installer and a JRE)

It is possible to put several custom module paths into the command --module-path. It is important to really put in the full path of the required modules, including the filename of the module itself. For instance, let's assume I want to include the modules module1.jar and module2.jar for the execution of MyApplication.jar, and all of these files are put into the same folder, say /my/custom/folder. Then the command line command would look like this:
java -jar --module-path /my/custom/folder/module1.jar:/my/custom/folder/module2.jar --add-modules module1,module2 MyApplication.jar

Related

Does the jmods directory in jdk/jmods have any role during application runtime?

I have an application which runs in a sandbox environment using an OpenJDK.
The JDK directory has a jmods folder.
The documentation regarding jmods isn't that great. From what I understand, jmods is useful if I want to create another custom JRE using JLink.
Say, if I don't want to do that and just want to have a JDK which can build and run my Java SWT application. Will it be safe to simply remove the jmods directory?
Does the jmods directory in jdk/jmods have any role during application runtime?
No. These are only archives that are used as input to jlink. The class files and other resources that are used during runtime are stored in the lib/modules archive, which is in a custom format that can be read for instance with the jimage tool. (or by JDK code using one of two jimage library implementations in the JDK)
.jmod files are not meant to be used during execution. If you try to put a .jmod file on the module path, you even get an error:
java.lang.module.FindException: JMOD format not supported at execution time: <some.mod>.jmod
Say, if I don't want to do that and just want to have a JDK which can build and run my Java SWT application. Will it be safe to simply remove the jmods directory?
If you use jlink to create a runtime image, it will not contain a jmods directory in the first place. So, there's nothing you should have to remove.

how to run another .jar file when the current program has a bundled jre?

Before when users were required to have a jre installed to run aps:
ProcessBuilder builder = new ProcessBuilder("java -jar execute.jar");
Now with jlink and jpackage being released .jars can be deployed with a bundled jre.
What is the correct method to start another .jar from a program that had been deployed with jlink/jpackage?
java -jar will not work as java is not installed on the end user anymore
see:
How to call an embedded jre from command line in order to run java applications
This is a similar problem but does not address creating the .jar with jlink/jpackage
If you have a main class defined, you should be able to run "java -jar HelloWorld.jar".
Otherwise seems like you can create a launcher pointing to a starting module.
From Redhat Reference:
https://access.redhat.com/documentation/en-us/openjdk/11/html/using_jlink_to_customize_java_runtime_environment/creating-custom-jre-modular
Create a custom JRE with the required modules and a custom application launcher for your application.
$ ./jdk-11/bin/jlink --launcher hello=sample/sample.HelloWorld --module-path sample-module --add-modules sample --output custom-runtime
$ ./custom-runtime/bin/hello
If you have to run the second JAR in its own JVM, then you have to have a JRE or JDK installed to do that.
Alternatively, you need to use jlink or jpackage or whatever to turn it into a self-contained application and distribute that instead of the second JAR. The user has to install that application in a place that your main application can find.
In either case, you run the second application as a separate process; i.e. as a separate JVM.
If it acceptable to run the second JAR in the current JVM, then it may be possible to do the following:
Open the JAR file to read its MANIFEST.
Fetch the Main-Class and Class-Path manifest attributes
Create a new classloader with a classpath that includes the second JAR and all of its dependencies. (This assumes that they are available ... at the locations specified by the Class-Path attribute.)
Use the classloader to load the main class.
Use reflection to find and call the main classes entry point method; i.e. the static void main(String[]) method. Pass it a String[] to represent the command line arguments.
There are a couple of problems with this approach.
The application you are running from the 2nd JAR will share the environment with your main application. The same standard input/output/error. The same current directory and environment variables.
If the second application misbehaves ... it can bring down the main application in various ways. A security sandbox might help, but it won't protect against everything.
If the main application has been distributed with a custom JRE, then it may not include all of the Java SE classes that the second application needs. If that is the case you will get classloader exceptions or errors.
Finally, this approach may not work at all if the packaging technology you use uses an AOT compiler to compile the main application + all classes that it uses to native code.
UPDATE - Now that I understand what you are actually doing here (you have a main app and an updater app), I think there is another way to do this. The jlink utility allows you to create a bundle which has multiple entry points. When this is installed, I would expect it to appear as commands with different names that are links to the same executable. They would (naturally) use the same embedded JRE.
Read the following for more information:
For jlink - Java Platform, Standard Edition Deployment Guide: Self-Contained Application Packaging: Supporting Multiple Entrypoints
For jpackage - Packaging Tool User's Guide: Support Application Requirements: Add Launchers
Note that doing it this way would address possible issues with functionality missing from the bundled JRE. The jlink or jpackage command should make sure that all classes / modules that are needed will be included.

JVM not found when launching a JavaFx app deployed with jpackage and installed

I've packaged a JavaFx (14) project with jpackage for deployment in .exe setup.
The project dependencies are:
Java 14.0.2
JavaFx 14.0.2.1
JRE 1.8.0_271 (removed)
So, during my deployment on several machines (approximately 6), it was successful on several, but on others I have this error:
JVM can not launch
although all the dependencies have been properly installed on these machines as well as the environment variables well defined.
Can someone help me about it
After some fixes (mentionned by #Slaw and #mipa, I generated a new package, install it. But when I run the software (myApp.exe), I've this error
Failed to lunch JVM
After some research about it, the solution was to create a batch file with java command that will run the .jar file located on the C:\Program Files\ software folder
cd "C:\Program Files\software\app"
java --module-path "C:\Program Files\Java\javafx-sdk-14.0.2.1\lib" --add-modules=ALL-MODULE-PATH --add-exports javafx.graphics/com.sun.javafx.sg.prism=ALL-UNNAMED -jar software.jar
I've done it and it run the software very well, but is there another optimize solution to run directly the software without the batch file?
Cause on some computers, the batch didn't run the software, returning that same error: Failed to lunch JVM
Please, help
If you have to manually launch the application with --module-path and --add-modules then you failed to include JavaFX while packaging the application, or at least failed to include JavaFX correctly. Keep in mind that JavaFX is modular, even if your code is not. So when you package your application you should tell jpackage to treat JavaFX as modules.
First, you should get the JavaFX JMOD files. You can get them in one of at least two ways:
Download the JMOD files from Gluon (the same place you downloaded the JavaFX SDK from).
Install and use a JDK that includes JavaFX (e.g. from Zulu Community, BellSoft Liberica, etc.).
With this option you don't need the JavaFX SDK to begin with.
You want the JMOD files because they package the native code (and other things, such as licenses) with the Java code. This allows jlink (used behind-the-scenes by jpackage) to gracefully include the native code with the custom run-time image. Also, don't forget that JavaFX (and by extension the JMOD files) is platform-specific. And jpackage can only create applications for the operating system it's invoked on.
After getting the JMOD files you'll want to package your application. Let's assume your code is not modular (you use -jar in your question) and you've packaged your code into a JAR file. The following command should be the minimum you need to use jpackage:
jpackage --type app-image --module-path <path-to-jmods> --add-modules <needed-modules>
--input <dir-containing-your-jar-file> --main-jar <your-jar-file>
--main-class <your-main-class> --dest <your-output-location>
Some notes:
If you install and use a JDK with JavaFX included then you won't need --module-path. At least not for JavaFX.
I'm not sure what modules your application needs (for the --add-modules option). You use ALL-MODULE-PATH but I doubt you need everything.
Not positive, but I believe if you have a Main-Class attribute in your JAR file's manifest then --main-class is not needed.
If you truly need the --add-exports argument then you can use:
--java-options="--add-exports javafx.graphics/com.sun.javafx.sg.prism=ALL-UNNAMED"
If you make your code modular you can also get rid of the --add-modules argument. Instead you'd put your module (and any modules not in the JDK) on the --module-path and replace --input, --main-jar, and --main-class with --module-path and --module.
In this case --input no longer seems necessary. However, if you had other files needed by your application (e.g. security policy files) then you may still need --input.
See the JPackage User Guide for more information. There are many ways to customize your application described in the guide that are not mentioned in this answer.
If all this still doesn't solve the "Failed to launch JVM" error on every computer then you're going to need to do further debugging. The first thing you want is to know the actual error causing the problem. I can think of at least two ways to do this:
Add logging to your application. Log to a file.
Configure jpackage to make your application console-based. Then launch the application from the console to see any output. If you're having issues seeing the output then you can redirect the standard/error streams to a file or use options that tell the console not to open a new window.

package javafx.util does not exist

I am running java version 11.0.2 on a Mac. WHen I compiled a java file that imports javafx.util.pair I got an error: package javafx.util does not exist. There are not many useful resources to solve this issue. I tried different suggestions online, but in no vain. Currently I am trying to add the JavaFX package to my Java directory. But it doesn't work.
Here is what I did:
Downloaded javafx-sdk-11.0.2 folder. Inside the folder, there are 2 directories - legal and lib
Moved items in legal dir to /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home/legal
Move items in lib dir to /Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home/lib
Reopen Terminal to compile this .java file.
Question_1: Did I miss anything above?
Question_2: If this approach is wrong, what would you suggest?
javafx.util.Pair is part of the javafx.base module, and as you have mentioned, you need to download the JavaFX SDK to your machine, as it is no longer part of the Java JDK anymore.
You can follow the OpenJFX docs on how you can get started.
Once you have downloaded the SDK, if you want to run JavaFX 11, you will see that you need to do something as documented:
export PATH_TO_FX=path/to/javafx-sdk-11.0.2/lib
javac --module-path $PATH_TO_FX --add-modules=javafx.controls HelloFX.java
java --module-path $PATH_TO_FX --add-modules=javafx.controls HelloFX
This means that you run your Java 11 and include the modules from the JavaFX SDK independent location. Note that javafx.controls has as transitive dependencies the javafx.base and the javafx.graphics modules.
However, you won't read in those docs that you have to copy the JavaFX files into the JDK. That won't work.
The main reason why it won't work: Java 11 and JavaFX 11 are modular, and even if you see the JavaFX jars under path/to/javafx-sdk-11.0.2/lib, the JDK uses a big file: /path/to/jdk-11.0.2.jdk/Contents/Home/lib/modules to run the java command. That file was created when the JDK was built, therefore adding any jar to it won't have any effect.
Alternative
However, you will find in the docs the right way to "copy" the JavaFX SDK into the JDK: by creating a new custom image. See the link, section Custom JDK+JavaFX image.
You can use jlink to create a runtime image that includes some or all the JavaFX modules, without being attached to a given project.
So you can create a combined image Java11+JavaFX11, and use that as your new JDK. This will allow you getting rid of the --module-path and --add-modules arguments (in terms of JavaFX at least).
In fact, some distributions like this one already do this.
So the options are: you use the regular JDK and the JavaFX SDK (with --module-path and --add-modules) or you create/use a custom JDK that includes JavaFX.

Manifest Classpath for Jar files

I created a small application which includes an external package called JavaFX. The package contains 4 jar file.
When I create a jar from the command line to the current directory, the jar executes OK (importing classes from the 4 jars). But when I move that jar file from a different directory, the file wont execute. In other words the created jar can't seem to locate the classes in those jar files (JavaFX jars) ?
Below is the manifest file:
Main-Class: QuizMenu
Class-Path:
deploy.jar
javaws.jar
jfxrt.jar
plugin.jar
The textfile does contain a space at the start and end of each line of the jars, and a final carriage return at the last line (Each line apart from the last line does not contain a carriage return). Also the Main-Class: header works OK. (Just the classpath doesn't seem to work).
Can anyone identify the problem with the file?
Recommendation
You should not refer to any of these (deploy.jar javaws.jar jfxrt.jar plugin.jar)
jar files in your mainifest file.
JavaFX Packaging Tools
Instead, you should use JavaFX aware deployment tools such as:
javafxpackager
JavaFX ant tasks
JavaFX maven plugin
JavaFX gradle plugin
NetBeans JavaFX project
Some other compatible tool which might come along in the future.
The above tools will appropriately package your application to use JavaFX.
Background
If you try to bundle the JavaFX, Java deployment and Java plugin jar files with your application, then when you run your application on a different Java version (such as Java 8 or 9), it may not function correctly as JavaFX in all future releases will be defined to be part of the Java runtime platform and not as a separately installed library or a library which can be bundled with an application separate from the rest of the Java runtime.
Additionally, jar files such a jfxrt.jar rely on numerous native dynamically linked libraries (.so, .dll, etc) which differ from platform to platform and have 32 bit and 64 bit variants. The version of jfxrt.jar must match the version of the underlying native libraries (and the underlying native libraries for the appropriate platform must be available to the dynamic load linker) in order for jfxrt.jar (e.g. JavaFX) to function correctly.

Categories