Gradle task to create fat runnable jar - java

I have a java application with Main class which has dependencies to couple of other library jars. I need to create a runnable jar in gradle with all dependant libraries copied to the jar. The gradle plugin "application" or "java" does not provide this. I am using latest gradle version 6.7

I could achieve this by creating a task which does the following things
Update manifest file with the Main-Class attributes
Copy all compile time dependencies to the jar task
plugins {
id 'application'
}
repositories {
mavenCentral()
//jcenter()
}
dependencies {
compile group: 'org.json', name: 'json', version: '20200518'
testImplementation 'junit:junit:4.13'
implementation 'com.google.guava:guava:29.0-jre'
}
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'com.example.gradle.App'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
} with jar
}
More details can be read in this article A simple java project with Gradle

Related

How to manage external dependencies of a Gradle Plugin

Gradle Version: 1.12/2.0 (restricted due to Org policies)
JDK: 1.8
I have created a custom gradle plugin that performs some installation using code that has been defined in another sub-project of our source-code. The build.gradle for the plugin is
dependencies {
compile project(path: ':installlib', configuration: 'libConfig')
compile project(path: ':tools', configuration: 'toolConfig')
}
jar {
from {
// LINE#1 source-code of plugin
sourceSets.main.output
zip64 = true
// LINE#2 dependencies
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
baseName = 'myCustomPlugin'
version = '1.0'
destinationDir = new File(project.libDir)
}
The above plugin is consumed as follows - consumer.gradle
buildscript {
repositories { flatDir name: 'libs', dirs: System.env.CODESOURCE + '/lib/')
dependencies {
classpath: ':myCustomPlugin:1.0'
}
}
apply plugin: 'java'
apply plugin: 'myCustomPlugin'
...
...
//rest of the items of this gradle
Case-A
If I run this, I hit > Plugin with id 'myCustomPlugin' not found.
NOTE: I have META-INF/gradle-plugins/myCustomPlugin.properties created correctly with implementation-class pointing to my plugin code and this works fine if I don't get into creating the fat/uber-jar business and just include the sourceSets.main.output statement in my jar task. But since our entire project depends on file based artifacts, I am attempting to create a fat/uber-jar and that's where I start running into these issues.
Case-B
If I move the LINE#2 above the LINE#1, which looks like -
from {
// LINE#2 dependencies
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
// LINE#1 source-code of plugin
sourceSets.main.output
zip64 = true
}
If I run this, I hit ClassNotFoundException for one of the classes coming from the project 'installlib'. I can see the *.class file in the uber jar created but even then the class loader complains about this class.
Could anyone provide some pointers on how to resolve this ? The uber/fat jar creation (or the way I am doing it) is not helping with the plugin-source-code and dependencies of the plugin.
If not a fat jar, could anyone provide some inputs on how to resolve the dependencies in the buildscript of the consuming gradle ?

How to build jar file with gradle with dependencies

I'm building a jar file with Gradle. This jar file is being used as a library in another project. But when the project tries to use the jar file, a ClassNotFoundException is returned.
Caused by: java.lang.NoClassDefFoundError: com/auth0/jwt/JWT
I've included the jwt library in the gradle file building the jar:
compile group: 'com.auth0', name: 'java-jwt', version: '3.4.0'
The project using the jar can't seem to find this jar dependency in the jar.
I built the jar with the gradle command:
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Jar File creation',
'Implementation-Version': version,
'Main-Class': 'com.group.me.name.MyJarClass'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'
}
How do I include the missing dependency in the jar?
You, generally, should not pack the dependencies inside a published JAR. It's better to declare them as dependencies in pom.xml and let the user of you JAR fetch them. It's just a good practice that should be enough.
If it's not enough, use Gradle Shadow:
plugins {
id 'com.github.johnrengelman.shadow' version '4.0.2'
}
Fat JAR is produced with shadowJar task in this case. Publishing is easy as well:
publishing {
publications {
shadow(MavenPublication) { publication ->
project.shadow.component(publication)
}
}
repositories {
maven {
url "http://your.repo"
}
}
}

Gradle building old version of code

I'm trying to build a fat jar with gradle but every time I do I get a really old version of the program. Running the program from main directly in IntelliJ works fine so it is something with the gradle build itself that is not working. When i check the jar in (project path)/build/libs the date and time of the file has changed so it did indeed build but when i start it i get a month old build. I suspect there might be some cache that is causing this but i do not know where that is located.
build.gradle
version '1.0.2'
apply plugin: 'java'
repositories {
mavenCentral()
}
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Example',
'Implementation-Version': version,
'Main-Class': 'com.example.Main'
}
baseName = project.name
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
jar {
manifest {
attributes(
'Main-Class': 'com.example.Main',
)
}
}
dependencies {
compile 'com.intellij:forms_rt:6.0.5'
compile project(':common')
testCompile group: 'junit', name: 'junit', version: '4.11'
sourceCompatibility = 1.7
targetCompatibility = 1.7
}
settings.gradle
rootProject.name = 'example'
include ':common'
project(':common').projectDir = new File(settingsDir, '../common')
Command
./gradlew fatjar
Well, I discovered the problem and it was a combination of things. The only thing that was actually outdated was the form ui. The reason for this was that IntelliJ started using binary class files for the forms instead of java source files.
To fix it go to settings then editor and after that GUI designer. Press Java source code instead of binary class files. Regenerate the design (might have to delete the generated code and run it again). Then build it with gradle, it should now work.

Fat jar built with Gradle doesn't include libraries

I'm trying to build a fat jar which would contain all .jar libraries included in 'libraries' folder.
Here is a snapshot of my build.gradle file:
group 'MyApp'
version '2.0'
apply plugin: 'java'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile files('libraries/ojdbc7.jar')
compile files('libraries/postgresql-42.1.4.jar')
compile files('libraries/db2jcc.jar')
}
tasks.withType(Jar) {
destinationDir = file("$rootDir/target")
}
jar {
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
manifest {
attributes 'Main-Class': 'MainLauncher'
}
processResources{
exclude '*'
}
archiveName 'myapp.jar'
}
All .jar libraries are included into 'libraries' folder located in root of the project.
For some reason my code works fine in IDE, however fails to execute some tasks when run as a standalone .jar file.
When I decompress the jar, I can see all content inside root folder including libraries which fail to be found.
Is there anything I'm doing wrong?

Add to subproject to gradle jar

How can I add a subproject referenced using project(':api') to the jar gradle builds?
This is the build.gradle of my main project. The subproject is includes as git submodule and has a similar buildscript.
apply plugin: 'java'
sourceCompatibility = 1.5
version = '1.0'
jar {
manifest {
attributes('Main-Class': '..........')
}
}
repositories {
mavenCentral()
}
dependencies {
compile files('libs/jfxrt.jar')
compile project(':api')
testCompile group: 'junit', name: 'junit', version: '4.11'
}
I figured it out on my own.
Include the source of a subproject in the main jar:
sourceSets {
main {
java {
srcDir project(':api').file('src/main/java')
}
}
}
Including the classes of a jar in the main jar:
jar {
from zipTree('libs/abc.jar')
}
Try to add classpath to your manifest file. You need to have directory (example below uses "lib") to keep jar files on which your project depends.
Try modifying your "jar" block in gradle build to something like this. I have some addition properties just for demonstration. But the important one is Class-Path
jar {
manifest.attributes(
'Class-Path': lib/api.jar
'Built-By': System.getProperty('user.name'),
'Built-JDK': System.getProperty('java.version'),
'Built-OS': System.getProperty('os.name'),
'Built-DATE': buildDate,
)
}
I hope it helps to fix your issue.
In the simplest case, a fat Jar can be created as follows:
main/build.gradle:
jar {
from configurations.runtime
}
There are other, more robust solutions, such as the gradle-one-jar plugin for "main" method style applications.

Categories

Resources