Gradle bootRepackage task in a project with multiple jars - java

I have a project using gradle and springboot. My project also produces an auxiliary jar of its test classes so that other projects can leverage them. This seems to send the springboot gradle plugin off the rails.
The docs on the plugin state:
The bootRepackage task depends on Gradle assemble task, and when executed, it tries to find all jar artifacts whose qualifier is empty (i.e. tests and sources jars are automatically skipped).
Due to the fact that bootRepackage finds 'all' created jar artifacts, the order of Gradle task execution is important.
What I'm seeing instead is that bootRepackage is just repackaging the most recently created jar, not all jars, as the documentation states. This jar happens to be my jar of test classes, which is obviously not what I want.
Looking through the config options, I can set bootRepackage.withJarTask to specify which jar-creating task to use. If I set that to "jar", bootRepackage will create the correct archive, but still chooses the name of the jar from my test task.
My minimal build script which demonstrates this is as follows.
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE")
}
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.google.inject:guice:3.0' // random dependency
}
task testJar (type: Jar, dependsOn: testClasses) {
baseName = "test-${project.name}" // somehow springboot uses this name
from sourceSets.test.output
}
configurations {
testArtifacts
}
artifacts {
testArtifacts testJar
}
springBoot.backupSource = false
bootRepackage {
withJarTask jar
classifier = "boot"
}
Assuming my project is named foo running bootRepackage with this script, I get a jar named test-foo-boot.jar instead of foo-boot.jar. Am I doing something improperly, or does that withJarTask not do exactly what it should?

Related

Gradle Run MainClass from external dependency

I have two gradle-configured projects, projectA and projectB. ProjectA is a spring boot project with two main classes, one run when running projectA directly, another run when running projectB (see gradle file below). ProjectA is exported as a JAR which is declared as a dependency of projectB. ProjectB specifies as its main class a class from projectA.jar:
//ProjectB's build.gradle
apply plugin: 'application'
mainClassName = 'com.projectA.api.MainClassToBeRunInProjectB'
/**
* `verify` task, `run` task to be exact
*/
task verify(dependsOn:run)
dependencies{
compile (group:'com.projectA.api',name:'api-ProjectA-jar',version:'1.0.0')
}
//Download artifacts
repositories {
mavenLocal()
maven {
url 'http://nexus.projectJarLocations.local/nexus/content/groups/public/'
}
mavenCentral()
}
When running the verify step in projectB, the build fails as follows:
acidnbass:projectB acidnbass$ gradle clean verify
> Task :run
Error: Could not find or load main class com.projectA.api.MainClassToBeRunInProjectB
FAILURE: Build failed with an exception.
Why can't I run the main class from projectA? Is there extra config needed to run a main class from an external source?
its possible by setting up a task like this:
task runApp(dependsOn: configurations.compileClasspath, type: JavaExec) {
group = "app"
classpath = sourceSets.test.runtimeClasspath
main = 'com.projectA.api.MainClassToBeRunInProjectB'
}
and by adding a dependency as an implementation
dependencies{
implementation('com.projectA.api:api-ProjectA-jar:1.0.0')
}

Rename shadow jar produced by shadow plugin to original artifact name

I am using gradle shadow plugin to build my uber jar.
build.grade file looks like:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
}
}
apply plugin: 'com.github.johnrengelman.shadow'
dependencies {
compile "com.amazonaws:aws-lambda-java-events:1.3.0"
}
assemble.dependsOn(shadowJar)
It produces following jars in build/libs folder.
myProject-1.0.0-SNAPSHOT.jar
myProject-1.0.0-SNAPSHOT-all.jar '//uber jar
I want to replace original jar with uber jar. How do i do this?
It isn't clear why want to do this, but I'm assuming you mean "with the original JAR's name". You should do 2 things:
Give a different classifer to the jar task (or archiveName, or the other properties that affect the name) or disable it so that you don't constantly overwrite it on every build and avoid doing unnecessary work
Change the classifier on the shadowJar task
The ShadowJar extends from the Gradle built-in Jar task, so most of the configuration options from that apply to the ShadowJar task.
tasks.jar.configure {
classifier = 'default'
}
tasks.shadowJar.configure {
classifier = null
}
For least keystrokes, without burning any bridges,
replace the line:
assemble.dependsOn(shadowJar)
with:
jar {
enabled = false
dependsOn(shadowJar { classifier = null })
}
Verify:
$ gradle assemble --console=plain
:compileJava
:processResources NO-SOURCE
:classes
:shadowJar
:jar SKIPPED
:assemble UP-TO-DATE
Perhaps disabling the jar task in build.gradle will work
apply plugin: 'java'
jar.enabled = false
So you will only have your uber jar.
You can do it in that way :
// save the old jar task
def oldJarTask = tasks.jar
// remove the original jar tasks from the tasks list
tasks.remove(jar)
// create a new task named "jar" thats depends on shadowJar
// when you will run jar task it will be actually run the shadow jar
task jar(dependsOn:[shadowJar])
// create a task to run the plain old good jar task from gradle :)
task oldJar(dependsOn: oldJarTask)
This was tested and worked, hoped it helped you!

How to install a compiled by Gradle jar into the local Gradle Repository files-2.1 instead of the Maven repository?

In my build.gradle, I added the plugin:
apply plugin: 'maven'
Then using gradle install I can copy the resulted jar into the maven repository : ~/.m2/repository
However, my Gradle repository resides in ~/.gradle/caches/modules-2/files-2.1. How can I install the jar into this repository?
What worked for me is gradle install -Dmaven.repo.local=the/path/of/the/folder.
I don't know which IDE you are using but in eclipse you can add a new Run Configuration, in Gradle taks add install and in program arguments -Dmaven.repo.local=the/path/of/the/folder.
If you insist on manipulating the cache, then your best bet is to write a shell script that will manually replace latest JAR in the cache.
The reason is that Gradle does not come with this functionality built-in, as Gradle uses notion of "local cache" in a strict sense, as opposed to "local repository" which is used by Maven.
The difference is that you are never supposed to save files to local cache manually.
To solve your problem the recommended way: Suppose that project A is a dependency of project B. Then you can call publishToMavenLocal command in project A to refresh the depedency. Add mavenLocal() repository in gradle.build of project B, so every time you build project B, Gradle will check the local repository when resolving the dependency A.
mavenLocal() is resolved like this:
In order to make use of a non-standard local maven repository, you can use the following configuration in your build.gradle:
repositories {
maven {
url '/Users/manuelj/apache/maven/repository'
}
}
A build.gradle sample to create a Jar file along with its logback dependencies. using mavenlocale()
apply plugin: 'java'
apply plugin: 'eclipse'
version = '1.0'
sourceCompatibility = 1.7
target Compatibility = 1.7
//create a single Jar with all dependencies
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': 'com.mkyong.DateUtils'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it :
zipTree(it)
}
}
with jar
}
//Get dependencies from Maven central repository
repositories {
mavenCentral()
}
//Project dependencies
dependencies {
compile 'ch.qos.logback:logback-classic:1.1.2'
}
Reference create a Jar file along with its logback dependencies.

Including Java library built with Gradle throws NoClassDefFoundError

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.

How to use Gradle to build a JAR that depends on another JAR

I have a Eclipse workspace with a declared workset configured to have several projects. Some are to generate JAR files and others are web applications that use those JAR files. In my architecture I have a JAR that will consist of domain core services and another one that depends on the first one that will consist of higher level services. Finally I will have some web applications that use those both JARs.
The first JAR project is build with Gradle, based on the following script
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'
repositories {
mavenLocal()
mavenCentral();
}
jar {
baseName = 'br.ufpr.unidades.dominio'
version = '0.1.0'
}
dependencies {
compile 'org.hibernate:hibernate-core:4.3.7.Final'
}
task wrapper(type: Wrapper) {
gradleVersion = '1.11'
}
As anyone can see, it´s a very simple build.gradle file and the build works fine with it. The expected JAR file is generated in the expected destination folder.
Now, here comes the build script for the second JAR:
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'
repositories {
mavenLocal()
mavenCentral()
}
jar {
baseName = 'br.ufpr.unidades.dominio.hibernate'
version = '0.1.0'
}
dependencies {
runtime fileTree(dir: '../dominio/build/libs', include: '*.jar')
compile 'org.hibernate:hibernate-core:4.3.7.Final'
}
task wrapper(type: Wrapper) {
gradleVersion = '1.11'
}
The second file is very similar to the first one, except it has a dependency on generated JAR:
runtime fileTree(dir: '../dominio/build/libs', include: '*.jar')
Eclipse doesn´t show any problems, but when I try to build the second JAR project I get many Class not found error messages, like the one below:
[sts] -----------------------------------------------------
[sts] Starting Gradle build for the following tasks:
[sts] build
[sts] -----------------------------------------------------
:compileJava
D:\Users\alex\Documents\Eclipse\workspace\unidades\dominio.hibernate\src\main\java\dominio\hibernate\HibernateCargoRepository.java:7: error: package unidades.dominio does not exist
import unidades.dominio.Cargo;
The message is clear: I´m importing a package that is not being found during the build, so the classes such a package has cannot be referenced in my code. Such a package is declared in the first and perfectly generated JAR file. It also is visible under Referenced Libraries item in the Eclipse project, so Gradle was able to find it to reference it in design time, but not to build the second JAR.
After all that, I suspect it´s a dependency management problem, but I can´t see which it is and how to fix it.
Thanks in advance!
Are You using classes from the jar under dominio/build/libs in the second project? If so, this should be a compile dependency. I'd also recommend setting a multimodule gradle project. Here are the docs.

Categories