Including Java library built with Gradle throws NoClassDefFoundError - java

I am writing a Java library and I would like to build the library with Gradle and then test it from a local test project.
I would prefer using Gradle 3.3 for my objective.
The library should be built for Java5 and higher.
So far my build.gradle looks like this:
plugins {
id 'jvm-component'
id 'java-lang'
}
repositories {
mavenCentral()
}
model {
components {
main(JvmLibrarySpec) {
sources {
java {
dependencies {
module 'commons-codec:commons-codec:1.10'
module 'org.apache.httpcomponents:httpcore:4.4.6'
module 'org.apache.httpcomponents:httpclient:4.5.3'
}
}
}
api {
exports 'io.simplepush'
}
targetPlatform 'java5'
}
}
}
The source code of the library is located in src/main/java/io/simplepush/Notification.java and depends on the dependencies stated in the build.gradle file.
Building the library with ./gradlew build works fine and generates build/jars/main/jar/main.jar.
However when I run a test project from IntelliJ (after including main.jar into the test project), I get the following runtime error:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/http/HttpEntity.
It seems like the test project does not know about the runtime dependencies needed by my library.
I am not sure on what is the correct way to tell the test project about the dependencies of my library.
I do not want a fat jar which includes all dependencies.
Listing all dependencies in the test project itself is also not an option.
Preferably I want the library itself to tell the test project about which dependencies it needs.

The library jar which you have created does not contain any dependency information which the IDE/Gradle can then resolve to be able to compile/run the test project. I see that you are using the maven central repository so what you need to do is to publish your library to your local maven repository and in the test project just add a dependency information (no just plain jar file).
So in both library and test project build.gradle add a maven local repository config.
repositories {
mavenLocal()
mavenCentral()
}
And now you need to publish the library to local repository. As you are using the gradle 3.3 you can use the Maven Publishing.
So in the library build.gradle add a maven publishing information.
publishing {
publications {
maven(MavenPublication) {
groupId 'io.simplepush'
artifactId 'project1-sample'
version '1.1'
from components.java
}
}
}
Gradle “maven-publish” plugin makes this easy to publish to local repository automatically creating a PublishToMavenLocal task.
So you can just run
gradle publishToMavenLocal
Which will publish your library with all the dependency information into local maven repository.
And then you just need to add a library information to you test projects build.gradle
dependencies {
// other dependencies .....
module 'io.simplepush:project1-sample:1.1'
}

I solved it by changing several things.
Thanks to #Babl for pointing me in the right direction.
My new library build.gradle looks like this:
plugins {
id 'java'
id 'maven-publish'
}
sourceCompatibility = 1.5
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile 'commons-codec:commons-codec:1.10'
compile 'org.apache.httpcomponents:httpcore:4.4.6'
compile 'org.apache.httpcomponents:httpclient:4.5.3'
}
publishing {
publications {
maven(MavenPublication) {
groupId 'io.simplepush'
artifactId 'project1-sample'
version '1.1'
from components.java
}
}
}
Now I can push the library to the local maven repository with ./gradlew publishToMavenLocal.
The build.gradle of the test project uses the application plugin and defines a main class (which is Hello in my case). Then I can run ./gradlew installDist to generate an executable file (see Application plugin docs) which puts all dependencies in the classpath and runs just fine.
group 'com.test'
version '1.0-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'application'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile 'io.simplepush:project1-sample:1.1'
}
mainClassName = "Hello"

This specify what repositories to check to fetch the dependencies from
repositories {
mavenCentral()
}
Therefore, anything that is in the dependecies{} will be fetched from those above.
If the test project is not coupled with the library project, (#RaGe example) new test project needs to know where to take the dependency from - you need to publish it, using preferred method.
After that, your new test project needs to specify the library with the preferred configuration (compile...runtime etc) in the build.gradle dependencies{}
After that depending on IDE you need to refresh the classpath and download the dependency from the specified before repository, the transitive dependencies specified in the library dependency (in this case) will get fetched from test projects repositories{}
Library build.gradle
repositories {
mavenCentral()
}
dependencies {
module 'commons-codec:commons-codec:1.10'
module 'org.apache.httpcomponents:httpcore:4.4.6'
module 'org.apache.httpcomponents:httpclient:4.5.3'
}
test project build.gradle
repositories {
mavenCentral() repository to fetch transitives
mavenLocal() or any other repo that you published the library to
}
dependencies {
pref-conf librarygroup:name:version
}
You can use idea or eclipse plugin in gradle for gradle idea or gradle eclipseClasspath tasks to refresh it with your freshly added dependencies.
With this solution, you should not need to pack the transitive dependencies within the library,
PS. I am just confused after you said you want executable jar.

Related

Gradle : How to bump transitive dependency version which is coming from plugin?

I have a huge java multimodule application which uses gradle to manage build and dependencies.
In one of the modules let's say module1 the project is using gretty plugin
module1/build.gradle
plugins{
id 'org.gretty'
}
gretty is having a transitive dependency on ch.qos.logback:logback-classic:1.1.3
I want to bump the logback version to the latest. For that I have tried below solutions
dependencies {
// 1 try
implementation 'ch.qos.logback:logback-classic:1.2.6'
// 2nd try
implementation ('ch.qos.logback:logback-classic:1.2.6') {
force = true
}
// 3rd try
constraints {
implementation('ch.qos.logback:logback-classic:1.2.6') {
because 'some xyz reason'
}
}
}
But none of this is having any impact on logback version. Need some suggestion now
What you have done so far is for application dependencies, not build dependencies. To change or add additional dependencies to the build itself, use the buildscript block. So for you case, to bring in a more recent version of Logback:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
mavenCentral()
}
dependencies {
classpath 'ch.qos.logback:logback-classic:1.2.6'
}
}
https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies
You can then invoke the buildEnvironment task to view the dependencies of the build.

How to prevent gradle from downloading SNAPSHOT jar

I am using a dependency module-x which has may or may not have SNAPSHOT version of trivial/other dependencies. When I build the application I wanted to make sure that all the trivial/other dependencies are of release type and not SNAPSHOT as SNAPSHOT keeps changing.
build.gradle file
dependencies {
implementation 'org.my-group-x:module-x:1.2'
}
it downloads a bunch of dependencies and it may have multiple dependencies which are of SNAPSHOT typed
module-y:2.0-SNAPSHOT.jar
module-z:3.1-SNAPSHOT.jar
module-k:2.7-SNAPSHOT.jar
How I can make sure it is rejected and not added to the application? Also i dont know the dependencies to exclude it specifically.
You can exclude dependencies in build tools, see:
https://docs.gradle.org/current/userguide/dependency_downgrade_and_exclude.html#sec:excluding-transitive-deps
You can also configure the Maven repositories used in Gradle, see:
https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:repository-content-filtering
Here is an example to use only release versions or only snapshot version:
repositories {
maven {
url "https://repo.mycompany.com/releases"
mavenContent {
releasesOnly()
}
}
maven {
url "https://repo.mycompany.com/snapshots"
mavenContent {
snapshotsOnly()
}
}
}

Gradle custom plugin jar with dependencies

I'm trying to build a jar for a custom gradle plugin to be used by other gradle projects. I'm using java to write the plugin. I'm having a problem including dependencies in my jar. If I build the jar using the below build.gradle
plugins {
id 'groovy'
}
repositories{
mavenCentral()
}
dependencies {
compile gradleApi()
compile localGroovy()
compile 'com.google.guava:guava:27.0-jre'
testCompile 'junit:junit:4.12'
//compile 'org.apache.commons:commons-lang3:3.8.1'
}
group = 'com.mine'
version = '1.0'
I get a NoClassDefFound exception for guava classes when applying the plugin on a project. If I include a task to create a jar with dependencies like below in the build.gradle
jar {
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it)}
}
}
It says Plugin with Id 'my-plugin' not found. How do I include dependencies in a gradle plugin jar?
Your plugin project should be configured as a standalone Plugin project and then published to a maven repository, which will make dependencies resolution work; there is good documentation about writing custom plugin here, specially the following part : using Gradle plugin development plugin
There is also a good example of writing/publishing/consuming a custom Plugin in the Gradle examples here : https://github.com/gradle/gradle/tree/master/subprojects/docs/src/samples/plugins (see the two subprojects publishing and consuming )
And here is a working example with a plugin that has dependency on external library (commons-lang for example):
Plugin project
build.gradle
plugins {
id 'java-gradle-plugin'
id 'groovy'
id 'maven-publish'
}
group 'org.gradle.sample.plugin'
version '0.1'
// pugin metadata configuration
gradlePlugin {
plugins {
myplugin {
id = "org.gradle.sample.plugin.myplugin"
implementationClass = "org.gradle.sample.plugin.MyPlugin"
}
}
}
// publish to local maven repo for testing
publishing {
repositories {
maven {
url "../repos/maven-repo"
}
}
}
// repo for dependences resolution
repositories{
jcenter()
}
// dependencies of this plugin
dependencies {
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.8.1'
}
Plugin implementation : src/main/groovy/org/gradle/sample/plugin/MyPLugin.groovy
package org.gradle.sample.plugin
import org.apache.commons.lang3.StringUtils
import org.gradle.api.Plugin
import org.gradle.api.Project
class MyPlugin implements Plugin<Project> {
#Override
void apply(final Project project) {
println "Applying custom plugin... "
project.tasks.create('testPlugin'){
doLast{
println " custom plugin task executing."
println "Result: " + StringUtils.capitalize("stringtotest")
}
}
}
}
Build and publish this plugin ./gradlew publish : the plugin jar and "plugin marker artefacts" will be published to local maven repo in ../repos/maven-repo
Consumer project
build.gradle
plugins {
id 'java'
// import/apply your custom plugin
id 'org.gradle.sample.plugin.myplugin' version '0.1'
}
group 'org.gradle.sample.plugin'
version '0.1'
repositories{
maven {
url "../repos/maven-repo"
}
jcenter()
}
To test the plugin, try to execute the plugin task testPlugin
> Task :testPlugin
custom plugin task executing.
Result: Stringtotest
Sorry to add this as an answer but I don't have enough points to comment (yes it is a bit late in coming but I found this in a search and it came so close, maybe this will help someone else).
The answer by #M.Ricciuti is correct, just missing one file, namely a settings.gradle in the referencing project (not the plugin) directory:
pluginManagement {
repositories {
maven {
url '../repos/maven-repo'
}
gradlePluginPortal()
ivy {
url '../repos/ivy-repo'
}
}
}
Many thanks, I have tried many things that didn't work before finding this, even the examples by gradle didn't work (or more likely I didn't run them correctly). Anyway I merged what I saw in the answers with M. Ricciuti's answer and saw that file in the sample.
My complete project is at https://github.com/reddierocket/sampleGradlePlugin
The readme has instructions to run it. (Note I did not include the wrapper but I am using gradle version 5.3.1.)

libGdx and facebook

I'm testing facebook login with libgdx project. In my project-level build.gradle file, I added the gdx-facebook dependency from here
project(":android") {
apply plugin: "android"
configurations { natives }
dependencies {
compile project(":core")
......
......
compile "de.tomgrill.gdxfacebook:gdx-facebook-android:1.4.1"
}
}
and added the following dependency in the Module:android build.gradle file.
dependencies {
compile 'com.facebook.android:facebook-android-sdk:[4,5)'
}
There is no error in such condition. But when I remove tomgrill dependency from project-build.gradle file, android studio show me the following alert and the project cannot run.
Gradle project sync failed. Basic functionality(e.g. editing,
debugging) will not work properly.
Why facebook dependency does not work without tomgrill gdx-facebook extension? What the problem is?
Artifact facebook-android-sdk available in mavenCentral repository so you've to add mavenCentral() in your project repositories list in root build.gradle file.
By default, In LibGDX projects mavenCentral() is in repo list.
Some transitive dependency in your project, Internally facebook-android-sdk using many support library like annotations, customtabs, appcompat-v7, support-v4 and more..
These support libraries are available on Google's Maven repository so you need to include Google's Maven repository in your top-level build.gradle file :
allprojects {
repositories {
google()
// If you're using a version of Gradle lower than 4.1, you must instead use:
// maven {
// url 'https://maven.google.com'
// }
// An alternative URL is 'https://dl.google.com/dl/android/maven2/'
}
}

gradle can't resolve nexus dependency

I have a dependency I'd like to include from a local nexus. I've seen several related problems that all say to do something like
apply plugin: "java"
apply plugin: "maven"
repositories {
maven {
url "http://my.url.com/"
}
}
dependencies {
compile "name:id:version"
}
I can access the nexus fine via web but when I try the above, I end up with a error message
Could not resolve: name:id:version
Any help would be greatly appreciated.
I found my problem. There were transitive dependencies I didn't catch as I was initially building from Eclipse without the --stacktrace option. I modified the build script such that
compile("name:id:version") {
exclude group: "another-name", module: "its-module"
// other dependencies to exclude ...
}
If by local repository you mean your local computer repository, then to ask Gradle to lookup dependencies from the local maven repository you should explicitly tell him to do so like this:
repositories {
mavenLocal()
}
Gradle is not like maven and is not using the local repository of the maven to cache downloaded artifacts. From the Gradle points of view, the local maven repository is also like other repositories.

Categories