Problem
Part of what Gradle does is dependency management. That means it knows what dependencies you need and how to find them (based on configurations in the build script). When you execute/build your application via Gradle the tool will automatically search repositories, download+cache dependencies, and place those dependencies on the class-path/module-path. Once you deploy your JAR file Gradle is no longer involved, so your deployment is responsible for including the needed dependencies.
In other words, you need to ship your application's dependencies with your application JAR file.
Solutions
You basically just need to make sure you include your application's dependencies with your application. Here are at least three ways to do that.
Copy Dependencies
Copy the dependencies into a build folder as part of the build process. Here's an example of such a task, using the Kotlin DSL:
tasks {
val jar by existing(Jar::class)
val copyDependencies by registering(Copy::class) {
from(configurations.runtimeClasspath)
into(jar.get().destinationDirectory)
}
jar.configure {
finalizedBy(copyDependencies)
}
}
Now if you execute ./gradlew jar
Gradle will create the JAR file and then copy the dependencies into the same directory as the JAR file. Then you just need to make sure all the JAR files are deployed together.
If I remember correctly, the default class-path is the working directory. But to specify the class-path you would use -cp
, -classpath
, or --class-path
when executing your application. The module-path, if needed, is set with -p
or --module-path
.
Fat JAR
Create a so-called "fat" or "uber" JAR file. That's a JAR file that includes not just your own application code but all your application's dependencies as well. You could configure the jar
task for this, but it would probably be easier for you to simply apply the Gradle Shadow Plugin.
// Kotlin DSL
plugins {
id("com.github.johnrengelman.shadow") version "<version>"
// other plugins...
}
And then you'd create the fat JAR with ./gradlew shadowJar
. See the user guide for more information.
Self-Contained Application
Create a self-contained executable using a tool like jpackage
. This tool gives you an application that has all its code and the JRE embedded, and then gives you an installer or native executable (e.g. exe
on Windows). Here's the user guide for jpackage
. There are Gradle plugins to make using jpackage
from Gradle easier, such as The Badass JLink Plugin.
Note jpackage
was added in Java 14 and was incubating until Java 16. Also note that jpackage
can't "cross-package". That is to say, if you build your application on Windows then you can only create installers/executables for Windows; same for MacOS and Linux. If you need to package for multiple platforms then you'll need access to each platform.
JavaFX
Since you've tagged this question with JavaFX I want to give a note of caution. Though if you're not using JavaFX 9+ then this is not relevant to you.
Technically JavaFX only supports being loaded as named modules. That means it needs to be placed on the module-path, either via --module-path
or by including it in the custom-runtime image built by jlink
/ jpackage
. As of JavaFX 16 a warning is now emitted if JavaFX is loaded from unnamed modules (i.e. the class-path).
Executable JAR files are placed on the class-path. That includes fat JARs. And if you are not using a JDK that includes JavaFX—meaning you have Gradle pull in the JavaFX dependencies—then JavaFX will be included in your fat JAR and be placed on the class-path. Now, despite not being supported and now emitting a warning, nothing seems to currently break if JavaFX is on the class-path. Except for one caveat: Your main class must not be a subclass of javafx.application.Application
. You'd have to create a separate main class that simply launches JavaFX.
Because of all this, I would highly recommend using jpackage
to deploy JavaFX applications. You may want to read this Q&A as well.