Correct options to package dependent classes with jpackage - java

I'm building an installer on Windows with a few classes in the application jar but with dependencies on logging (logback). This is not a modular application. I'm finding it difficult to get it built properly since the logging dependencies are not being built into the installer. When it runs, cmd.exe tells me that the slf4j classes are not being found.
This is the build command
jpackage --verbose --dest jpk -p ..\.m2\repository\org\slf4j\slf4j-api\1.6.6\slf4j-api-1.6.6.jar;..\.m2\repository\ch\qos\logback\logback-core\1.0.9\logback-core-1.0.9.jar;..\.m2\repository\ch\qos\logback\logback-classic\1.0.9\logback-classic-1.0.9.jar --win-dir-chooser --win-console --input ..\webspeed\target --type msi --main-jar webspeed-1.0-SNAPSHOT.jar --main-class com.technojeeves.webspeed.WebspeedSystray
I'm hoping it's not necessary to have a fat jar as the starting point, since when I tried that after using Maven Shade, I found that jpackage took ages and a massive amount of memory. I couldn't wait any longer and terminated the build.
There are very few examples of jpackage per se, other than simple ones that just echo the Oracle docs. I couldn't find a single one for non-modular with deps.

jpackage runs jlink implicitly for module dependencies but does not seem to include the jars.
For builds with non-module jars you can add each jar under your --input directory parameter as a build step before the jpackage command. Create a staging input directory ..\webspeed\target\jars and copy each of the non-module jars to that staging directory.
After running jpackage and then the installer: check the classpath entries are set by inspecting the yourlaunchername.cfg file. Each launcher cfg should contain an entry which indicates that the classpath is setup with installed copies of each non-module jars.
For example a Windows installer should show:
[Application]
app.classpath=$APPDIR\jars\logback-core-1.0.9.jar
app.classpath=$APPDIR\jars\logback-classic-1.0.9.jar
...
If/as module dependencies don't change much it is usually quicker/easier to pre-prepare a jlink image containing the module images and pass in --runtime-image ${your.jlink}.

Related

Can I put javafx modules directly into a runnable JAR?

I am working on a project that uses Javafx, and my goal is to make a .exe file with Launch4J, because it will be used by other people.
For now, when I export the project in a runnable JAR, if I want to execute it, I have to write as an argument the path of the javafx folder and add the different modules :
java --module-path javafx-sdk-17.0.2\lib --add-modules javafx.controls,javafx.fxml -jar test13.05.jar
Is there a way to put the javafx folder in my projects lib folder, and add the arguments directely into the main function so I can execute the project without having to write the path and add the modules ?
Fat jars are not recommended to distribute JavaFX application for various reasons. Instead use the official jpackage tool to package your application.
solution with a script (linux ubuntu version)
Adding jvm arguments as command in .sh script. every path must be relative in order to run it in other linux pc .Nescesary javafx jar are in libs folder
run_demo.sh
#! /bin/bash
java --module-path libs --add-modules javafx.controls -jar demo-1.0-SNAPSHOT.jar
This aproach can be implemented in other os with their proper scripts

executing runnable JAR on PC's without JDK

I am having an executable JAR. Ofcouser I have JDK installed at my end I am giving following command to run my exe JAR from command prompt.
1 Using JRE :-
C:\Users\userName\Desktop\Utility\latest>"C:\Program Files\Java\jre1.8.0_161\bin\java.exe" -jar Utility.jar
2 Using JDK
C:\Users\userName\Desktop\Utility\latest>"C:\Program Files\Java\jdk1.8.0_161\bin\javaw.exe" -jar Utility.jar
Both are working on my desktop but if I tries #1 to run the executable JAR on different machine which has only JRE Version (1.8 onwards) it is not getting opened up.
I tried following links but some links are sayin to download few installers but all I do not want to get that. Is there any way. Or issue with my executable JAR ?
How can I make my executable JAR not need JDK to run
Run a JAR file using a specific JRE
Manifest-Version: 1.0
Rsrc-Class-Path: ./ commons-collections4-4.3.jar poi-3.17.jar poi-ooxm
l-3.17.jar xmlbeans-3.0.1.jar curvesapi-1.06.jar poi-ooxml-schemas-3.
17.jar poi-examples-3.17.jar poi-excelant-3.17.jar poi-scratchpad-3.1
7.jar commons-codec-1.10.jar commons-collections4-4.1.jar commons-log
ging-1.2.jar curvesapi-1.04.jar junit-4.12.jar log4j-1.2.17.jar xmlbe
ans-2.6.0.jar ooxml-schemas-1.3.jar
Class-Path: ./ commons-collections4-4.3.jar poi-3.17.jar poi-ooxml-3.17.jar
xmlbeans-3.0.1.jar curvesapi-1.06.jar poi-ooxml-schemas-3.17.jar poi-examples-3.17.
jar poi-excelant-3.17.jar poi-scratchpad-3.17.jar commons-codec-1.10.jar
commons-collections4-4.1.jar commons-logging-1.2.jar curvesapi-1.04.jar
junit-4.12.jar log4j-1.2.17.jar xmlbeans-2.6.0.jar ooxml-schemas-1.3.jar
Rsrc-Main-Class: DataProcessor.DataProcessor.App
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoade
It sounds like the issue your are having is more than not having the JDK or knowing where the JRE on the target system is located, it's that you also didn't include the dependencies that your code has.
The jar file you have includes details in the manifest file that tells the JVM the classpath and the main class to load. If you look at the Rsrc-Class-Path, it is add the jars from the current directory. This is fine if you are sending the entire folder structure that includes all the jars in the expected location. But it doesn't work with just the jar.
In order to make a single jar that would run without any additional jars, you need to repackage the jars. There are two common ways to do this
UberJar - where the class of your project is combined with the classes extracted form all of your dependencies into a single jar
JarInJar - where your jar and all the dependecy jars are put into a jar and a custom classloader is used to load the classes from the jars inside the jar.
I'm not sure what build tool you're using, but for Maven the Shade Plugin will create an UberJar.
I personally recommend using the JarInJar option for this reason. The Spring Boot Maven Plugin is what I believe to the easiest

What are auto executable jars?

I was going through spring-boot-maven-plugin documentation and came across a term auto executable jar.
Could someone please explain me what is an auto executable jar and how is it different then normal jar files and how they are auto executed?
spring-boot-maven-plugin documentation mentions the term but does not go further to explain it
repackage: create a jar or war file that is auto-executable. It can replace the regular artifact or can be attached to the build lifecycle with a separate classifier.
Could someone please explain me what is an auto executable jar
A fully executable jar can be executed like any other executable
binary or it can be registered with init.d or systemd. This makes it
very easy to install and manage Spring Boot applications in common
production environments.
So In conclusion is like any other executable when you use a executable jar
how is it different then normal jar files and how they are auto executed?
Well a java file you need to run with java -jar
From Spring Docs
The Maven build of a Springboot application first build your own application and pack it into a JAR file.
In the second stage (repackage) it will wrap that jar with all the jar files from the dependency tree into a new wrapper jar archive. It will also generate a Manifest file where is defined what's the application Main class is (also in the wrapper jar).
After mvn package you can also see 2 jar files in your target directory. The original file and the wrapped jar file.
You can start a Springboot application with a simple command like:
java -jar my-springboot-app.jar
I may suggest that auto executable means that you supplied main method so that it can be launched with java -jar options, otherwise it may be just a java library.
Here is a quote from https://docs.spring.io/spring-boot/docs/current/maven-plugin/repackage-mojo.html
Repackages existing JAR and WAR archives so that they can be executed from the command line using java -jar. With layout=NONE can also be used simply to package a JAR with nested dependencies (and no main class, so not executable).
Executable jar - the one that has main class declared in manifest and can be run with java -jar yourJarFile.jar command
Other jars - jars jars without delcared main calss. Can be anything - application, library, etc. Still can run application by providing fully.qualified.class.name as entry point like java -cp yourJarFile.jar my.bootstrap.BootstrapClass
Autoexecutable jars - never heard about it :)

How to open JavaFX .jar file with JDK 11?

I created a JavaFX project in IntelliJ.
I can run project in IntelliJ. I added below code in Configurations):
--module-path ${PATH_TO_FX} --add-modules=javafx.controls,javafx.fxml
But the output .jar file of project (made with Artifects) doesn't run. I tested these commands, but didn't get any chance:
java --module-path %PATH_TO_FX% --add-modules javafx.controls,javafx.fxml -jar Timer.jar
java --module-path %PATH_TO_FX% --add-modules javafx.controls Timer.jar
Last error log of command line:
Error: Could not find or load main class Files\Java\javafx-sdk-11.0.1\lib
Caused by: java.lang.ClassNotFoundException: Files\Java\javafx-sdk-11.0.1\lib
p.s: I could run .jar file of this project when build on JDK-10
EDIT:
I downloaded JavaFX and added it's lib folder to System Environments.
for adding JavaFX to project I did this process:
Project Structure > Libraries > add > Java > JavaFxPath/lib
Then I created Artifect for output jar file in this process:
Project Structure > Artifects > Add > JAR > From Modules with dependencies > main Class : main.Main.
Providing you have a simple (non-modular) JavaFX 11 project (without Maven/Gradle build tools), and you are using IntelliJ, like the HelloFX sample from here,
this is how you can create a jar from IntelliJ that can be run from the console
A full tutorial on how to run the project can be found here, and instructions on how to create a jar are here (see section Non-modular project), but these doesn't cover Artifacts from IntelliJ.
Check that the HelloFX project runs from IntelliJ with these VM options:
--module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.fxml
where PATH_TO_FX has been set in File -> Settings -> Appearance & Behavior -> Path Variables, pointing to the JavaFX SDK lib.
Semi fat Jar
We can create a Jar that only contains the classes from the project, and third party dependencies, but not JavaFX ones.
Go to File -> Project Structure -> Artifacts -> Add -> JAR -> From modules with dependencies, add your main class, accept.
Then remove the JavaFX jars from the list, and accept.
Build the project, it will create a quite small jar (3 KB in this case).
Now you should be able to run it like:
java --module-path %PATH_TO_FX% --add-modules javafx.controls,javafx.fxml -jar out\artifacts\HelloFX_jar\HelloFX.jar
(make sure that %PATH_TO_FX% points to a valid folder and use quotes if it contains spaces.
You can distribute this jar, and run it in other platforms, providing those also have the JavaFX SDK.
Fat Jar
If you want a full fat jar that includes JavaFX dependencies, you can still use Artifacts.
Go to File -> Project Structure -> Artifacts -> Add -> JAR -> From modules with dependencies, add your main class, accept.
Then keep the JavaFX jars from the list, and accept. Build the project.
In theory, you should be able to run it like:
java -jar out\artifacts\HelloFX_jar\HelloFX.jar
But this won't work.
Reason 1: You need a launcher class, as explained here.
So create a launcher class:
public class Launcher {
public static void main(String[] args) {
Main.main(args);
}
}
Reason 2: If you only add your SDK jars to the fat jar, you will be missing the native libraries, as explained here.
So edit the artifact, select the Launcher class as main class, and add the native libraries (Directory Content -> path-to/JavaFX SDK/bin on Windows):
Now build the project (now the jar is about 33 MB, and contains unnecessary native libraries) and run:
java -jar out\artifacts\HelloFX_jar\HelloFX.jar
You can distribute this jar, but only to Windows platforms.
You can create similar jars for other platforms, if you download their JavaFX SDKs, and you can also build cross-platform jars if you add them all together, as explained in the linked answers above.
Anyway, you should consider using jlink instead.
Note
About this error:
Caused by: java.lang.ClassNotFoundException: Files\Java\javafx-sdk-11.0.1\lib
it looks like the library path was set without quotes and it is missing the first part of the path C:\Program Files\.... Just make sure you use quotes:
set PATH_TO_FX="C:\Program Files\Java\javafx-sdk-11.0.1\lib"
I had the similar issue exporting/generating an Jar using JavaFX and IntelliJ Non-modular with Gradle (https://openjfx.io/openjfx-docs/)
The jar I was generating using Gradle jar command does not run and throws error saying it can not find my Main Class. When I opened my jar I was able to locate my main class. So I realized the error has to do with Jar Packaging.
I fixed the problem by adding JavaFX SDK to my Java SDk in IntelliJ as shown below.
After this I use the regular Gradle build Jar command to generate my Jar file (as shown below) and it runs Normally.
The easiest way to do this is to use an OpenJDK build that include JavaFX. Both Bellsoft and Azul produce such builds.
For Azul's Zulu builds of OpenJDK:
https://www.azul.com/downloads/zulu-community/?version=java-11-lts&package=jdk-fx
For Bellsoft Liberica JDK:
https://bell-sw.com/pages/downloads/#/java-11-lts
and choose "Full JDK"
These builds are basically OpenJDK with the OpenJFX JavaFX modules added. Though be careful as some aspects of JavaFX may not be supported on LibericaFX. See https://bell-sw.com/pages/liberica-release-notes-11.0.9.1/
The above answers apply to non-modular JavaFX projects. To get a modular JavaFX project (with modular dependencies) running I used jlink.
Take the JavaFX SDK from https://gluonhq.com/products/javafx/; unzip the files to a directory, and point a shell variable JFX_LIB to the unzipped lib/ directory.
Take the JavaFX jmods (not SDK) file from https://gluonhq.com/products/javafx/; unzip the jmods, they are in a folder similar to javafx-jmods-11.0.2/. Point a shell variable JMOD_PATH to the unzipped directory javafx-jmods-11.0.2/.
Compile, sending your compiled classes to the directory mods/:
javac -d mods --module-source-path src --module-path $JFX_LIB/javafx.graphics.jar:$JFX_LIB/javafx.base.jar:$JFX_LIB/javafx.controls.jar --module com.mymodule
Create a custom JRE with jlink, referring to the module path containing the JavaFX mods and your own compiled mod:
jlink --module-path $JMOD_PATH:mods --add-modules com.mymodule --output customjre
Run:
customjre/bin/java --module com.mymodule/com.mymodule.Main
(I tried for a LONG TIME to get a java -jar running with --module-path and --add-modules. I always got the following error:)
Error: Could not find or load main class com.mymodule.Main
Caused by: java.lang.NoClassDefFoundError: javafx/application/Application

How to run Gradle JAR against local dependencies?

When I run gradle clean jar it pulls down all my executable JAR's dependencies under ~/.gradle and then creates my JAR.
I am then having to jump through serious hoops to run the JAR locally:
First I create a lib directory in the same folder as the newly-created JAR
Then I have to cherry pick all of my project's transitive dependencies (there are lots) out from ~/.gradle and copy them to lib
Then I run from the command-line java -jar myapp.jar -cp "lib/*"
Every time my dependencies change (new code is added, etc.) I have to go through this process, and it makes me wonder if there is a more elegant way to run your Gradle-built apps locally.
I ended up going with the application plugin. It turns out that Gradle considers compile dependencies a subset of runtime so I didn't need to change any dependency scopes. I can now just issue gradle run and it runs my app beautifully.

Categories

Resources