Successful Gradle project build produces NoClassDefFoundError at runtime - java

I am using Gradle to build my Java project in Eclipse. gradle.build is as follows
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile group: 'com.flowpowered', name: 'flow-nbt', version: '1.0.0'
compile group: 'org.reflections', name: 'reflections', version: '0.9.10'
}
All libraries are functioning properly when run through Eclipse. But sometimes it is useful to work on the command line. When run on the command line, the runtime error Exception in thread "main" java.lang.NoClassDefFoundError: com/flowpowered/nbt/regionfile/SimpleRegionFileReader occurs, even though the build is successful and the code contains imports from those libraries. I have tried cleans and rebuilds, along with gradlew build --refresh-dependencies, but I still encounter the same runtime error.
I would assume that the libraries are just never actually imported? Or that they are not being stored where the java project thinks they are? I'm unfamiliar with Gradle, so any advice on this is welcome.

Based on the posted build.gradle file you are not packaging the application as an executable JAR.
First apply the application plugin. But this will not be enough as you won't be able to run the executable as a single JAR without all of its dependencies. Apply the shadow plugin too.
These two plugins will give you access to the following tasks:
run: execute the application from gradle's command line.
runShadow: execute the application but with all dependencies packaged in a single JAR, alongside your compiled classes and resources.
shadowJar: create a single "fat JAR" with compiled classes and all dependencies.
Thus your build.gradle may look like this
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '1.2.4'
}
mainClassName = 'com.acme.YourMainClassName'
repositories {
mavenCentral()
}
dependencies {
compile group: 'com.flowpowered', name: 'flow-nbt', version: '1.0.0'
compile group: 'org.reflections', name: 'reflections', version: '0.9.10'
}
Plugin documentation:
https://github.com/johnrengelman/shadow
https://docs.gradle.org/3.4/userguide/application_plugin.html#useApplicationPlugin

Another solution without using any plugins and still end up with a runnable fat jar
jar {
archiveName = 'NameOfYourApp.jar'
manifest {
attributes 'Main-Class': 'uk.co.cdl.Main',
'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' '),
'Implementation-Version': project.version
}
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
include/exclude anything if need to if not take the curlys off
}
}

Related

Gradle dependencies not being exported to runnable jar by eclipse

I am trying to export my gradle eclipse project to a runnable jar, however it's dependencies are not being bundled into the runnable jar itself.
I file -> export -> runnable jar -> Extract required libraries into generated jar.
They are correctly exported if I manually do "add external jar" from the configure build path option, however I want to do this via gradle, and they are not correctly exported without the manual add.
I have tried to do a gradlew clean, and gradlew build. I have refreshed my gradle dependencies, and I have rebuilt the project, as well as cleaned it via the eclipse project -> clean option.
Below is my build.gradle file:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.4.RELEASE")
}
}
plugins {
id 'java'
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-activemq")
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'org.json:json:20171018'
compile 'org.mongodb:mongo-java-driver:3.6.4'
compile group: 'com.github.jai-imageio', name: 'jai-imageio-core', version: '1.4.0'
compile files ('../libs/CommonUtils.jar')
compile files ('../libs/UMS.jar')
compile files ('../libs/Cache.jar')
}
jar {
from { configurations.runtime.collect { zipTree(it) } }
}
When I look into the jar generated, I see that it doesn't contain the dependencies inside it, and when I go to run the jar via java -jar jar.jar, I get a NoClassDefFound error. I want to get all my gradle dependencies bundled into a runnable jar so I can do a java -jar jar.jar and have my jar run as if I had hit run in the eclipse editor.
The build tools itself will create the runnable jar.
gradle clean
gradle taskName
Project Jar will be created under folder :
$project/build/libs/ folder.

java 10 gradle project : automatic module not found

I created a java 10 project with intelliJ, using gradle.
I copied some stuff into it (some "AppFx" class using the library guava and javaFx, and a personal build.gradle file).
I also added a module-info.java file in src/main/java with this content:
module biblio5.main {
requires javafx.graphics;
requires javafx.controls;
requires javafx.base;
requires guava;
}
in which grava is an automatic module.
here is the relevant part of build.gradle:
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'com.google.guava:guava:23.0'
}
intelliJ can compile the project (using the hammer-like icon) but when I run the compileJava gradle task from intelliJ, I get an error:
13:12:46: Executing task 'compileJava'...
Task :compileJava FAILED C:\Users\lolve\Documents\gradle_java\biblio5\src\main\java\module-info.java:5:
error: module not found: guava
requires guava;
^ 1 error
I spent a lot of time on the net but did not manage to find an answer.
thank you
ps: here is the entire build.gradle:
buildscript {
dependencies {
classpath group: 'de.dynamicfiles.projects.gradle.plugins', name: 'javafx-gradle-plugin', version: '8.8.2'
classpath 'eu.appsatori:gradle-fatjar-plugin:0.3'
}
repositories {
maven {url "https://mvnrepository.com/artifact/de.dynamicfiles.projects.gradle.plugins/javafx-gradle-plugin"}
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
jcenter()
}
}
plugins {
id 'java'
id 'application'
id 'edu.sc.seis.launch4j' version '2.4.4'
}
apply plugin: 'javafx-gradle-plugin'
apply plugin: 'eu.appsatori.fatjar'
group 'lorry'
version '1'
sourceCompatibility = 1.10
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
maven {url "https://mvnrepository.com/artifact/de.dynamicfiles.projects.gradle.plugins/javafx-gradle-plugin"}
jcenter()
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
compile 'com.google.guava:guava:23.0'
}
//********************************************************************************************
launch4j {
outfile='bibliotek-v3.exe'
mainClassName = 'lorry.AppFx'
icon = "${projectDir}\\icons\\hands2.ico"
copyConfigurable = project.tasks.fatJar.outputs.files
//jar = "lib/${project.tasks.fatJar.archiveName}"
//headerType = "console"
jar = "${buildDir}\\productFatJar\\fat.jar"
}
jar {
baseName = 'executable3'
version = ''
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Main-Class': 'lorry.AppFx'
)
}
}
task copyExecutable(type: Copy) {
from file("${buildDir}\\launch4j\\bibliotek-v3.exe")
into file("c:\\Users\\lolve\\Documents\\gradle_java\\produits")
}
task copyJar(type: Copy) {
from file("${buildDir}\\jfx\\app\\bibliotek-v3.jar")
into file("c:\\Users\\lolve\\Documents\\gradle_java\\produits")
}
task copyFatJar(type: Copy) {
from file("${buildDir}\\productFatJar\\fat.jar")
into file("c:\\Users\\lolve\\Documents\\gradle_java\\produits")
}
createExe.doLast{
tasks.copyExecutable.execute()
}
task createJar(){
doLast{
tasks.jfxJar.execute()
tasks.jfxNative.execute()
tasks.copyJar.execute()
}
}
jfx {
jfxMainAppJarName = "bibliotek-v3.jar"
// minimal requirement for jfxJar-task
mainClass = 'lorry.AppFx'
// minimal requirement for jfxNative-task
vendor = 'lolveley'
}
fatJar {
destinationDir=file("${buildDir}\\productFatJar")
archiveName="fat.jar"
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Main-Class': 'lorry.AppFx'
)
}
}
task createFats(){
doLast{
tasks.fatJar.execute()
tasks.copyFatJar.execute()
tasks.createExe.execute()
}
}
EDIT
well, I made the change, and now I have "com.google.commons" instead guava in module-info.java, but I still get this error:
Testing started at 14:20 ... 14:20:14: Executing task 'check'...
Task :compileJava FAILED C:\Users\lolve\Documents\gradle_java\biblio5\src\main\java\module-info.java:5:
error: module not found: com.google.common
requires com.google.common;
^ 1 error
I changed gradle in intelliJ (the default option - recommended - was "default gradle wrapper") to my local gradle (v4.9), but without any effect.
What do you mean by "compatible with java"? What about try with a java 9 installation?
Update: Gradle 6.4 added basic support for Jigsaw modules. See this sample in the documentation (which also links to other related documentation). Note that the Building Java 9 Modules article linked to in this answer has changed significantly since this answer was posted.
The issue is Gradle still (as of 4.10-rc-2) doesn't have first-class support for Jigsaw modules. All the tasks will use the classpath, not the modulepath, when executing. This obviously will cause issues when trying to create a modular library/application (with module-info.java).
If you want to use Jigsaw modules in your project you should read Building Java 9 Modules. Your scenario, as #nullpointer mentions, is best covered by this section of the linked document. The takeaway is to add the following to your build.gradle file:
ext.moduleName = 'your.module'
compileJava {
inputs.property('moduleName', moduleName)
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath
]
classpath = files()
}
}
They also have sections for modifying the compileTestJava task (here) and the test task (here). Personally, I tend to not modify those tasks as testing often requires a lot of reflection which in turn requires a lot of --add-opens arguments. If you find out that's not true (haven't tried it in a while) or there's a better way, please let me know.
If your Gradle project is an application you also want to read the section covering the run and assemble tasks.
There is an experimental Gradle plugin that does all this for you: experimental-jigsaw. The plugin is limited, however, and there is a fork on GitHub, named chainsaw, that adds more features. Note: I don't know how maintained either plugin is.
Another Gradle plugin is available: Gradle Modules Plugin.
If you want to watch for updates regarding Jigsaw support in Gradle they maintain an epic on GitHub.
Also, to include what #nullpointer commented, you should be using a version of Guava that includes an Automatic-Module-Name entry in its manifest. Without this entry (combined with no module-info) the name of the module is subject to the name of the jar file; which may change unexpectedly. In other words, the Automatic-Module-Name entry makes for a better contract regarding the name of an automatic module. The first version that Guava added this entry is 23.2:
Changelog
Added JPMS module name com.google.common for Guava.
...
However, the most recent version (as of writing this answer) is 26.0.
More information about automatic modules can be found:
in the Javadoc of ModuleFinder.of(Path...)
this section of The State of the Module System
and this Stack Overflow question

Build is successful but the runtime dependency seems bad in Gradle dependency

dependencies {
compile 'org.slf4j:slf4j-api:1.7.13'
compile group: 'org.apache.commons', name: 'commons-math3' , version: '+'
testCompile 'junit:junit:4.12'
}
Even if I add this, when I run gradle build, it works, and codes with commons-math3 can be compiled. But when I run a jar file in build/,
it says Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/math3/complex/Complex
But the official Gradle site says, the resource in 'compile' will also be included in 'runtime' dependency. and I also tried adding the commons-math to runtime. but it does not work.
Maybe this is my misunderstood of the dependency system.
How can I include external library from maven repository into a jar file made by the Gradle.
What you are looking for is either the distribution zips produced by the application plugin or the shadow jar (also called fat jar) produced by the shadowJar plugin:
The distribution zip (application plugin)
About the distribution zip
The distribution zips look like this:
my-app-0.2.0.zip
├──bin
│ ├──my-app
│ └──my-app.bat
└──lib
├──my-app-0.2.0.jar
├──slf4j-api.1.7.13.jar
└──commons-math3-3.6.jar
You can then run your application with its dependencies by unzipping what has been produced in build/distributions/ and running either my-app.bat (on windows) or ./my-app (on linux or OS X)
Building a distribution zip
Here is a sample gradle build file for making a distribution zip:
build.gradle
apply plugin: 'java'
apply plugin: 'application'
mainClassName = 'org.myapp.Main'
repositories { jcenter() }
dependencies {
compile 'org.slf4j:slf4j-api:1.7.13'
compile 'org.apache.commons:commons-math3:3.6'
testCompile 'junit:junit:4.12'
}
Can be run with gradle distributionZip or gradle distributionTar. To just run the application, use gradle run.
The shadow jar
About the shadow jar
The shadow jar is one giant jar file that is a combination of your program and its libraries, packed together into one file. You will get a file that is self-contained and can be run by a double-click on most systems (e.g. on Windows that works, on Xubuntu it can be run by right-clicking and selecting 'Run with Oracle Java 8 Runtime', etc...).
Building a distribution zip
Here is, again, a sample build.gradle file:
apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'
mainClassName = 'org.myapp.Main'
jar {
manifest {
attributes('Main-Class': mainClassName)
}
}
buildscript {
repositories { jcenter() }
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.2'
}
}
repositories { jcenter() }
dependencies {
compile 'org.slf4j:slf4j-api:1.7.13'
compile 'org.apache.commons:commons-math3:3.6'
testCompile 'junit:junit:4.12'
}
Run it with gradle shadowJar - Your jar with packed dependencies will be in build/libs and it will be named my-app-x.x.x-all.jar.
Gradle is first of all a build tool (just like maven, btw).
Its "responisiblity" starts when you feed it a source file and ends when you get your artifact (in your case its a jar).
Now when you're going to actually run your application there is a plethora of different options here.
If you just run java -jar <your_jar> you are responsible by yourself to construct the classpath.
If you run it with some kind of external runner, you should read the documentation of it and supply it a classpath.
Hope this helps

How do I include a single dependency in my JAR with Gradle?

I'm starting with Gradle and I was wondering how do I include a single dependency (TeamSpeak API in my case) into my JAR so that it could be available at the runtime.
Here is a part of my build.gradle :
apply plugin: 'java'
compileJava {
sourceCompatibility = '1.8'
options.encoding = 'UTF-8'
}
jar {
manifest {
attributes 'Class-Path': '.......'
}
from {
* What should I put here ? *
}
}
dependencies {
compile group: 'org.hibernate', name: 'hibernate-core', version: '4.3.7.Final'
compile group: 'org.spigotmc', name: 'spigot', version: '1.8-R0.1-RELEASE'
// Many other dependencies, all available at runtime...
// This one isn't. So I need to include it into my JAR :
compile group: 'com.github.theholywaffle', name: 'teamspeak3-api', version: '+'
}
Thanks for your help :)
The easiest way is to start with a separate configuration for the dependencies you want to include. I know you only asked about a single jar but this solution will work if you add more dependencies to your new configuration. Maven has a well known name for this sort of thing called provided, so that is what we will use.
configurations {
provided
// Make compile extend from our provided configuration so that things added to bundled end up on the compile classpath
compile.extendsFrom(provided)
}
dependencies {
provided group: 'org.spigotmc', name: 'spigot', version: '1.8-R0.1-RELEASE'
}
jar {
// Include all of the jars from the bundled configuration in our jar
from configurations.provided.asFileTree.files.collect { zipTree(it) }
}
Using provided as the name of the configuration is also important because when the jar gets published, any dependencies you have in the providedconfiguration will show up as provided in the POM.xml that gets published with the JAR. Maven dependency resolvers will not pull down provided dependencies and users of your jar will not end up with duplicate copies of classes on the classpath. See Maven Dependency Scopes

intellij build jar artifact containing gradle dependencies

I basically want to do something simple - or atleast i think it should be pretty simple.
My goal is to create an Intellij gradle project, add some dependencies to the module using gradle and add some java source code to it.
Then I just want to have an option to somehow compile the whole thing into 1 jar, containing all grade dependencies and being able to execute using "java -jar"
However it turned out that this is not as easy is i had thought.
I just created a new gradle project from intellij and added a Main class.
I´ll give you an overview over my files:
settings.gradle:
rootProject.name = 'gradleTestNewJar'
build.gradle:
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'application'
sourceCompatibility = 1.6
version = '1.0'
repositories {
mavenCentral()
}
mainClassName = "com.randomPackage.StarterClass"
dependencies {
compile 'org.seleniumhq.selenium:selenium-java:2.46.0'
testCompile group: 'junit', name: 'junit', version: '4.11'
}
main class:
package com.randomPackage;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
public class StarterClass {
public static void main(String[] args){
System.out.println("test");
WebDriver driver = new HtmlUnitDriver(BrowserVersion.FIREFOX_38);
driver.quit();
}
}
The main method of "MyStart" is executed when running from Intellij via debug.
So it works, when all dependencies get loaded correctly.
NOTE: I use Intellij Community Edition if this makes any difference.
What i tried:
1. I tried to just use "gradlew clean build".
This created a jar, but without libs.
But I didn´t expect it to be as easy as this.
2. I tried to build an artifact of the module as suggested here:
http://blog.jetbrains.com/idea/2010/08/quickly-create-jar-artifact/
I tried it with extracted and not extracted dependencies.
In both cases the dependencies were added into the jar, but they were added to the root of the jar.
When i tried to run the jar file via "java -jar", it complained:
"Exception in thread "main" java.lang.NoClassDefFoundError: org/openqa/selenium/WebDriver
..."
OK, so it couldn´t load the dependencies.
NOTE: I thought that the dependencies were not added to the classpath, but i am not sure about this. However, i would expect Intellij to add dependencies to the classpath( or declare in the manifest file)
3. I also tried to use the gradle application plugin.
However this creates a zip/tar which contains a execute script and a bin folder which was not my intention.
So i started googling for hours and hours but i cann´t find a solution to my problem.
Come on this cannot be so hard - it is just so basic.
I am sure some genius can help me out and point me to my - probably stupid - failure.
My current solution is as follows:
I use gradle to build a jar containing all libs, I do this witha custom task called fatJar.
Here is a part from my build.gradle
apply plugin: 'java'
jar {
manifest {
attributes("Manifest-Version": "1.0",
"Main-Class": "com.randomPackage.MainClass");
}
}
task fatJar(type: Jar) {
manifest.from jar.manifest
classifier = 'all'
from {
configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
} {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
with jar
}
Then I just execute "gradle fatJar" on the command line and get a perfect jar.

Categories