gradle fatjar include classes from nested Jar - java

My problem might be simple to gradle experts.
I am building fatJar. One of the dependencies contain Jar file itself. With following piece of code fat jar is being created with all other dependencies but the one that has jar, it just places jars in final jar. Which makes sense. How can I copy all the classes from those nested jars into final Jar. Any help would be highly appreciated.
jar {
from {configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }} {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
}

Related

A library corrupts classpath?

There's a strange issue I've never seen.
Adding a compile 'org.locationtech.spatial4j:spatial4j:0.7' to the dependencies list in my gradle project leads to a corrupt classpath. When I comment out that library and run java -verbose:class -jar sol_backend_full.jar > ok.log it outputs 4399 lines of class entries. However, with that library in classpath, java -verbose:class -jar sol_backend_full.jar > failed.log outputs only 953 lines, most of which are java.lang.* or sun.*.
It obviously results in Error: Could not find or load main class.
➥ Has anyone ever encountered that strange behaviour?
Of course, I can substitute that library with another spatial library, but what's happening is simply strange. It happens only with this library, removing/adding any other is fine.
Gradle version in question is 5.5.1, and that library manifest looks a bit long, but not suspicious at all. Falling back to 4.8 also reproduces it.
Here is the build script:
task customFatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'ru.rxproject.sol.backend.BackendApplication',
'Implementation-Version': version + System.getenv('BUILD_NUMBER').toString(),
'Commit-Hash': 'git-' + System.getenv('GIT_COMMIT'),
'Build-Date': java.time.LocalDateTime.now().toString()
}
archiveName = 'sol_backend_full.jar'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
The JAR dependancy org.locationtech.spatial4j:spatial4j:0.7 is a signed jar. When you create a fat jar, java Classloader is not able to load the other classes from your fat jar because these are not signed.
So, you can't create a fat jar with that dependancy without excluding the signatures.
Please refer - Gradle - FatJar - Could not find or load main class
Like mentioned in the above post, you may exclude the signatures like -
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'
}
But, I would suggest to keep the jar dependancy out of the fat jar.

Alternative to fat jar in gradle java project

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.)

Gradle Jar type task doesn't package .class files from src/main

I defined a task "exeJar" with Jar type to package an executable jar, shown as below.
task exeJar(type: Jar, group: 'build', dependsOn: classes) {
description 'Package an executable jar.'
archiveClassifier.set('exe')
manifest {
attributes 'Main-Class': mainClassName
}
from {
configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
}
}
And I make the jar task exclude the main class.
jar{
exclude 'com/example/*class'
}
jar: packages an artifactId-version.jar file containing only the resources from src/main and excluding the main class.
exeJar: packages an executable artifactId-version-exe.jar file.
But the artifactId-version-exe.jar only contains the dependencies and excludes the resources from src/main directory, shown in the picture below.
Can someone explain why this happens and give a possible solution to make artifactId-version-exe.jar contain the main class?

Gradle - FatJar - Could not find or load main class

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.

Including specific external library to a jar file built by Gradle

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

Categories