I have started on gradle today itself. So I am trying out random things. I have below build.gradle file:
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter-api:5.4.2')
testRuntime('org.junit.jupiter:junit-jupiter-engine:5.4.2')
compile 'com.googlecode.json-simple:json-simple:1.1.1'
}
test {
useJUnitPlatform()
}
jar {
manifest {
attributes(
'Main-Class': 'src.main.java.demo.Hello'
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it)}
}
}
I have read somewhere that the method that I am using inside from block will create fat jar, which is not a good practice. As I am fairly new to this, I humbly want to ask the alternative to this. Is it adding classpath to manifest or something else??
The primary purpose is to bundle third party dependency jar files that will be used on runtime.
Thank You!
You could put all your dependencies into a ZIP file together with your own JAR and possibly external resources, or build an installer, or (if you're building a web app) build a WAR or EAR file. Gradle will take care of the manifest for you in any case.
(Also, there is nothing really wrong with fat JARs, they are fairly common these days and they do work.)
I have a project having multiple modules say utils and core.
settings.gradle
include 'utils', 'core'
core/build.gradle
jar {
destinationDir = file(LIB_DIR)
manifest {
attributes(
'Main-Class': 'com.cohesity.spotlight.Main'
)
}
}
dependencies {
compile files("$buildDir/libs/utils.jar")
}
The core.jar is containing classes from utils.jar. Why is this happening?
I think I know this one! (I've been baffled by it myself)
I'm guessing there are java source files packed inside utils.jar. By default javac will also compile these "implicit" sources alongside your sources from src/main/java. To stop this you'll want to pass -implicit:none to the compiler.
Eg:
tasks.withType(JavaCompile) {
options.compilerArgs << '-implicit:none'
}
See https://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html (search for "implicit")
I know that question was asked a lot and has many answers, but i still get it and I don't understand why...
I am trying to generate a .jar from a projet with dependencies with gradle.
I have a class src/main/java/Launcher.java, in which I have my main method.
there is my build.gradle
plugins {
id 'java'
id 'application'
}
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
mainClassName = 'Launcher'
repositories {
mavenCentral()
}
dependencies {
compile 'commons-io:commons-io:2.1'
compile 'io.vertx:vertx-core:3.4.0'
compile 'io.vertx:vertx-web:3.4.0'
compile 'com.google.code.gson:gson:1.7.2'
compile "com.auth0:java-jwt:3.1.0"
compile 'org.mongodb:mongo-java-driver:3.4.1'
compile 'com.google.guava:guava:24.1-jre'
compile 'commons-io:commons-io:2.6'
}
jar {
manifest {
attributes "Main-Class": mainClassName
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
I use $>gradle assemble to generate my jar
then $>java -jar path/to/my/.jar
And i get the error "could not find or load main class Launcher"...
I dont understand why, when I look in the .jar, I have Launcher class and in META-INF I have my manifest
Sorry for still asking this question in 2018 but i'm loosing my mind trying to figure out what's wrong. I hope somone will have the answer !
I reproduced your issue locally.
Just add exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA' to the jar task.
This will exclude the signatures of interfering dependencies.
Example:
jar {
manifest {
attributes "Main-Class": mainClassName
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
exclude 'META-INF/*.RSA'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
}
You are running into the the one major problem when building a FAT JAR:
One of your source JARs is signed and merging it into one fat jar destroys the signature.
It looks like Java recognizes that there are unsigned classes and ignores everything but the signed classes. As all classes that do not belong to the signed library are unsigned (like your Launcher class) they are ignored and therefore can't be loaded.
In your case it looks like that the dependency org.bouncycastle:bcprov-jdk15on:1.55 of com.auth0:java-jwt:3.1.0 is the signed jar file. Because my sample project correctly executes Launcher when I uncomment this dependency.
Bouncy castle is a crypto provider that requires a valid signature otherwise it will not run from my experience. Therefore it is impossible to create a fat jar for your project that just contains all classes.
You can try to create a fat jar with everything except Bouncycastle and ship Bouncycastle JAR seperatly.
Or a fat jar that contains all the required JAR files inside (JAR inside JAR) and that uses a special class loader that is able to load classes from within such a JAR inside a JAR. See for example: https://stackoverflow.com/a/33420518/150978
Try to exclude .SF .DSA .RSA files, example below, Nipun
Hope this works out for you
task customFatJar(type: Jar) {
baseName = 'XXXXX'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
with jar
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
manifest {
attributes 'Main-Class': 'com.nipun.MyMainClass'
}
}
Adding
exclude 'META-INF/*.RSA'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
Fixed my issue.
I am trying to convert a multi module project build process from Ant to Gradle.
We have a common module which is used by every other module. In common module I need these dependencies to be able compile it (by gradle build)
dependencies {
api 'com.google.guava:guava:18.0'
api 'org.json:json:20131018'
implementation 'org.apache.httpcomponents:httpclient:4.5.5'
implementation 'org.jruby:jruby-complete:1.5.1'
implementation 'org.python:jython:2.2.1'
implementation 'com.google.code.gson:gson:2.6.2'
}
Some of modules should include all dependencies needed in runtime inside result jar files. Because of this I am adding the code of module dependencies to the jar file as well. Like below:
dependencies {
compile project(':core:common')
compile project(':core:installer')
}
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
}
The problem is that I want to add external libraries to the jar file as well so that I have compelete jar file. It is possible to add external libraries by adding a line to jar above like this:
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
But then I will have a large jar file containing all of dependencies in common module while some of them are not needed in my specific jar file. What I want is to add specific external libraries to jar file for example I want just add 'org.json:json:20131018' and 'org.apache.httpcomponents:httpclient:4.5.5' to library and ignore rest of dependencies. I couldn't find a solution for this.
I solved this by adding the code below to jar:
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
def libraries =['httpclient-4.5.5.jar','json-20131018.jar']
from configurations.runtimeClasspath.
findAll { libraries.contains(it.name)}.
collect { zipTree(it) }
}
But I think that still Gradle should offer a better solution to include or exclude external libraries to jar file.
I updated solution above little bit which I think it is a better way to do this because we don't need specify jar files. We can define a configuration like this:
configurations {
runtimeLibraries
}
dependencies {
compile project(':core:common')
compile project(':core:installer')
runtimeLibraries 'org.apache.httpcomponents:httpclient:4.5.5', 'org.json:json:20131018'
}
Then we can update Jar task as below:
jar {
from sourceSets.main.output
from project(':core:common').sourceSets.main.output
from project(':core:installer').sourceSets.main.output
from configurations.runtimeLibraries.collect { zipTree(it) }
}
One diffference in result of those two methods is that with defining configuration gradle will recognize needed dependency and will add them to jar as well. For example if you are using commons-net:commons-net:1.4.1 as part of runtimeLibraries in created jar files you can find org.apache.oro packages which is used by commons-net
Until now I created runnable JAR files via the Eclipse "Export..." functionallity but now I switched to IntelliJ IDEA and Gradle for build automation.
Some articles here suggest the "application" plugin, but this does not entirely lead to the result I expected (just a JAR, no start scripts or anything like this).
How can I achieve the same result Eclipse does with the "Export..." dialog?
An executable jar file is just a jar file containing a Main-Class entry in its manifest. So you just need to configure the jar task in order to add this entry in its manifest:
jar {
manifest {
attributes 'Main-Class': 'com.foo.bar.MainClass'
}
}
You might also need to add classpath entries in the manifest, but that would be done the same way.
See http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
If you already have defined an application context, you can re-use the definition rather than duplicate it:
application {
// Define the main class for the application.
mainClass = 'com.foo.bar.MainClass'
}
jar {
manifest {
attributes 'Main-Class': application.mainClass
}
}
Both JB Nizet and Jorge_B's answers are correct.
In its simplest form, creating an executable JAR with Gradle is just a matter of adding the appropriate entries to the manifest. However, it's much more common to have dependencies that need to be included on the classpath, making this approach tricky in practice.
The application plugin provides an alternate approach; instead of creating an executable JAR, it provides:
a run task to facilitate easily running the application directly from the build
an installDist task that generates a directory structure including the built JAR, all of the JARs that it depends on, and a startup script that pulls it all together into a program you can run
distZip and distTar tasks that create archives containing a complete application distribution (startup scripts and JARs)
A third approach is to create a so-called "fat JAR" which is an executable JAR that includes not only your component's code, but also all of its dependencies. There are a few different plugins that use this approach. I've included links to a few that I'm aware of; I'm sure there are more.
shadow
one-jar
spring-boot
capsule
Least effort solution for me was to make use of the gradle-shadow-plugin
Besides applying the plugin all that needs to be done is:
Configure the jar task to put your Main class into manifest
jar {
manifest {
attributes 'Main-Class': 'com.my.app.Main'
}
}
Run the gradle task
./gradlew shadowJar
Take the app-version-all.jar from build/libs/
And finally execute it via:
java -jar app-version-all.jar
As others have noted, in order for a jar file to be executable, the application's entry point must be set in the Main-Class attribute of the manifest file. If the dependency class files are not collocated, then they need to be set in the Class-Path entry of the manifest file.
I have tried all kinds of plugin combinations and what not for the simple task of creating an executable jar and somehow someway, include the dependencies. All plugins seem to be lacking one way or another, but finally I got it like I wanted. No mysterious scripts, not a million different mini files polluting the build directory, a pretty clean build script file, and above all: not a million foreign third party class files merged into my jar archive.
The following is a copy-paste from here for your convenience..
[How-to] create a distribution zip file with dependency jars in subdirectory /lib and add all dependencies to Class-Path entry in the manifest file:
apply plugin: 'java'
apply plugin: 'java-library-distribution'
repositories {
mavenCentral()
}
dependencies {
compile 'org.apache.commons:commons-lang3:3.3.2'
}
// Task "distZip" added by plugin "java-library-distribution":
distZip.shouldRunAfter(build)
jar {
// Keep jar clean:
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
manifest {
attributes 'Main-Class': 'com.somepackage.MainClass',
'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ')
}
// How-to add class path:
// https://stackoverflow.com/questions/22659463/add-classpath-in-manifest-using-gradle
// https://gist.github.com/simon04/6865179
}
Hosted as a gist here.
The result can be found in build/distributions and the unzipped contents look like this:
lib/commons-lang3-3.3.2.jar
MyJarFile.jar
Contents of MyJarFile.jar#META-INF/MANIFEST.mf:
Manifest-Version: 1.0
Main-Class: com.somepackage.MainClass
Class-Path: lib/commons-lang3-3.3.2.jar
This is for Kotlin DSL (build.gradle.kts).
Method 1 (no need for application or other plugins)
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
// OR another notation
// manifest {
// attributes["Main-Class"] = "com.example.MyMainClass"
// }
}
If you use any external libraries, use below code. Copy library JARs in libs sub-directory of where you put your result JAR. Make sure your library JAR files do not contain space in their file name.
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
manifest.attributes["Class-Path"] = configurations
.runtimeClasspath
.get()
.joinToString(separator = " ") { file ->
"libs/${file.name}"
}
}
Note that Java requires us to use relative URLs for the Class-Path attribute. So, we cannot use the absolute path of Gradle dependencies (which is also prone to being changed and not available on other systems). If you want to use absolute paths, maybe this workaround will work.
Create the JAR with the following command:
./gradlew jar
The result JAR will be created in build/libs/ directory by default.
Method 2: Embedding libraries (if any) in the result JAR (fat or uber JAR)
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree) // OR .map { zipTree(it) }
from(dependencies)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Creating the JAR is exactly the same as the previous method.
Method 3: Using the Shadow plugin (to create a fat or uber JAR)
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these will be reflected for Shadow as well
tasks.jar {
manifest.attributes["Main-Class"] = "org.example.MainKt"
}
Create the JAR with this command:
./gradlew shadowJar
See Shadow documentations for more information about configuring the plugin.
Running the created JAR
java -jar my-artifact.jar
The above solutions were tested with:
Java 17
Gradle 7.1 (which uses Kotlin 1.4.31 for .kts build scripts)
See the official Gradle documentation for creating uber (fat) JARs.
For more information about manifests, see Oracle Java Documentation: Working with Manifest files.
Note that your resource files will be included in the JAR file automatically (assuming they were placed in /src/main/resources/ directory or any custom directory set as resources root in the build file). To access a resource file in your application, use this code (note the / at the start of names):
Kotlin
val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
// Alternative ways:
// val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
// val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
// val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
Java
var stream = MyClass.class.getResource("/vegetables.txt").openStream();
// OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
var reader = new BufferedReader(new InputStreamReader(stream));
var vegetables = reader.lines().collect(Collectors.joining("\n"));
You can use the SpringBoot plugin:
plugins {
id "org.springframework.boot" version "2.2.2.RELEASE"
}
Create the jar
gradle assemble
And then run it
java -jar build/libs/*.jar
Note: your project does NOT need to be a SpringBoot project to use this plugin.
Have you tried the 'installApp' task? Does it not create a full directory with a set of start scripts?
http://www.gradle.org/docs/current/userguide/application_plugin.html
Thank you Konstantin, it worked like a charm with few nuances. For some reason, specifying main class as part of jar manifest did not quite work and it wanted the mainClassName attribute instead. Here is a snippet from build.gradle that includes everything to make it work:
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '1.2.2'
}
...
...
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
...
...
mainClassName = 'com.acme.myapp.MyClassMain'
...
...
...
shadowJar {
baseName = 'myapp'
}
After running gradle shadowJar you get myapp-{version}-all.jar in your build folder which can be run as java -jar myapp-{version}-all.jar.
You can define a jar artifact in the module settings (or project structure).
Right click the module > Open module settings > Artifacts > + > JAR > from modules with dependencies.
Set the main class.
Making a jar is then as easy as clicking "Build artifact..." from the Build menu. As a bonus, you can package all the dependencies into a single jar.
Tested on IntelliJ IDEA 14 Ultimate.
I checked quite some links for the solution, finally did the below mentioned steps to get it working. I am using Gradle 2.9.
Make the following changes in your build,gradle file :
Mention plugin:
apply plugin: 'eu.appsatori.fatjar'
Provide the Buildscript:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
}
}
Provide the Main Class:
fatJar {
classifier 'fat'
manifest {
attributes 'Main-Class': 'my.project.core.MyMainClass'
}
exclude 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.SF'
}
Create the fatjar:
./gradlew clean fatjar
Run the fatjar from /build/libs/ :
java -jar MyFatJar.jar
Here is the solution I tried with Gradle 6.7
Runnable fat Jar (with all dependent libraries copied to the jar)
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'com.example.gradle.App'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
} with jar
}
Runnable jar with all dependencies copied to a directory and adding the classpath to the manifest
def dependsDir = "${buildDir}/libs/dependencies/"
task copyDependencies(type: Copy) {
from configurations.compile
into "${dependsDir}"
}
task createJar(dependsOn: copyDependencies, type: Jar) {
manifest {
attributes('Main-Class': 'com.example.gradle.App',
'Class-Path': configurations.compile.collect { 'dependencies/' + it.getName() }.join(' ')
)
}
with jar
}
How to use ?
Add the above tasks to build.gradle
Execute gradle fatJar //create fatJar
Execute gradle createJar // create jar with dependencies copied.
More details : https://jafarmlp.medium.com/a-simple-java-project-with-gradle-2c323ae0e43d
Configure Main Class to your Manifest
If you are using gradle project, just add the following into your build.gradle
jar {
manifest {
attributes(
'Main-Class': 'pokerhandscorer.PokerHandScorer'
)
}
}
Where 'pokerhandscorer' is the name of the package name,
and PokerHandScorer is the main class name
This creates a jar file into your \build\libs{jarFilename}.jar
Run jar file using java -jar /{path}/{jarFileName.jar}
java -jar /{path}/{jarFileName.jar}