Gradle dependency between subprojects - java

I am trying to figure out the proper way to declare a project dependency in gradle, i have this two subprojects:
A java app
apply plugin: 'java'
sourceSets {main {java {srcDirs = ['./']}}}
dependencies{compile project(':p2')}
jar {manifest.attributes(
'Class-Path': configurations.runtime.files.collect { it.name }.join(''),
'Main-Class': 'Main'
)
}
and a java library:
apply plugin: 'java-library'
sourceSets {main {java {srcDirs = ['./']}}
}
jar {
manifest.attributes(
'Class-Path': configurations.runtime.files.collect { it.name }.join(' ')
)
}
The root project is empty and the settings just include the subprojects.
The dependencies in the p1 project only tells that p2 must be built before p1, but what about configuring p2 as a lib for p1? Right now if a run p1 i got:
Exception in thread "main" java.lang.NoClassDefFoundError: StaticClass
Gradle build is fine:
C:\...>gradle build
BUILD SUCCESSFUL in 1s
4 actionable tasks: 4 up-to-date
I have to copy and paste p2.jar to the p1.jar directory for it to run properly, how can i get gradle to do it for me?

If you're trying to pack the classes from p2 into the p1 jar, try adding this to p1's build.gradle:
jar {
from project(':p2:').sourceSets.main.output
}
This is a bit unusual, though. Normally, if you've set up a separate library project, you pack it in a separate jar and add it to the classpath of dependent projects, so it's reusable.
Setting the Class-Path on the jar's manifest won't work the way you've written it. The manifest attribute interprets each entry as a file path relative to the working dir of the JVM, but you're just giving it the name of each jar, and Gradle doesn't do any copying or moving of files when setting up configuration classpaths. It's generally not a good idea to rely on the manifest to set the classpath, as it will only work if your jars are arranged on the filesystem exactly as as the manifest expects it. If this is really what you want, then you need to set up an 'installation' directory containing all the required jars arranged as expected. The Gradle Application Plugin can probably help you to achieve this, or something equivalent, but I've never used it.

Depending on your exact needs, you should have a look at the java-library-distribution plugin or the application plugin.
The first one will package a jar and its dependencies in an archive.
The second one will do the same and allow you to configure the main jar to be executable.

Related

Gradle Jar works as intended, but Gradle Run fails

I recently wanted to build jars of my JavaFX project with Gradle. So I went ahead and created the wrapper in my project directory and edited my build.gradle file to the below.
apply plugin: 'java'
apply plugin: 'application'
sourceSets.main.java.srcDirs = ['/']
mainClassName = "Main"
task wrapper(type: Wrapper) {
gradleVersion = '4.8'
}
jar {
manifest {
attributes(
'Class-Path': '../',
'Main-Class': 'Main'
)
}
from('/') {
include 'images/**/*.png'
include 'images/**/*.jpg'
include 'styles/css/**/*.css'
include 'fonts/**/*.TTF'
include 'fonts/**/*.ttf'
}
}
Using this, my compiled jar works as intended. No errors. But whenver I run gradlew runI get the following error.
Caused by: java.lang.NullPointerException
at styles.java.TitleStyles.<init>(TitleStyles.java:9)
at scenes.TitleScene.<init>(TitleScene.java:34)
at scenes.SceneController.<clinit>(SceneController.java:6)
... 14 more
The code in question is
private String stylesheet = this.getClass().getResource("/styles/css/TitleStyles.css").toExternalForm();
and my project structure looking like this
Project Structure Picture
Project Structure Picture #2
Any ideas on why I'm unable to do gradlew run?
The reason you are hitting a NullpointerException when you use gradle run is because the folder structure "/styles/...." is not in the classpath
To verify this .. you can ad this line in your Main.java :
System.out.println(System.getProperty("java.class.path")) ;
and run it as both java -jar
and gradle run
and you will see the difference.
Tp solve this , use the standard gradle java folder structure :
src
- main
--- java
--- resources
It was indeed a classpath issue, but there was a few more things I had to do to resolve my problems. I had to recreate my gradle project with the following structure.
src
-main
--java
---projectCodeHere
--resources
---nonJavaFileCodeHere
I had to relocate my non-java code to resources, otherwise it wasn't copied along with the .class files.
And then I had to change my code to look a bit like thisP
private String stylesheet = getClass().getClassLoader().getResource("css/ConnectionStyles.css").toExternalForm();
Everything worked correctly after that. It seems Gradle and IntelliJ are picky about the structure of project, and I couldn't find a "clean" solution to changing classpaths, so I ended up just reorganizing my project and am I ever the happier for it. Thanks for the help!

How to execute a class with main method which is present in fatJar created by gradle

This is a follow up question for how to execute a built fat JAR as a gradle task?
I don't have enough points to ask my question as a comment. So I have to ask again. This question has been asked in other formats and scenarios multiple times but none of those responses helped me.
My problem:
scenario 1 : create a single jar with dependencies using gradle fatJar
scenario 2 : create a single jar with dependencies using maven assembly
Execute
java -cp sample.jar com.example.ClassA
on jar files generated in both processes.
Issue:
jar from Process 1 gives me
Error : Could not find or load main class com.example.ClassA
jar from Process 2 executes correctly.
I have extracted both jar files and both of them have the same folder structure and same files - meaning the compiled class files are present in both jar files.
I haven't specified any manifest entries in either process because I have multiple main classes and I am not trying to generate an executable jar file.
My build.gradle file looks like below:
apply plugin: 'java'
apply plugin: 'maven'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenLocal()
}
sourceSets {
main{
java{
srcDirs 'src/main/java'
}
}
}
processResources {
from 'src/main/resources'
}
task compile (type: JavaCompile) {
source = sourceSets.main.java.srcDirs
include '**/*.java'
classpath = sourceSets.main.compileClasspath
destinationDir = sourceSets.main.output.classesDir
}
compile.options.compilerArgs = ["-sourcepath", "$projectDir/src/main/java"]
dependencies {
.
.
.
}
task fatJar(type:Jar) {
baseName = 'xxxxxxx'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
defaultTasks 'clean', 'compile', 'fatJar'
EDIT - 1:
I have tried relative path to the jar, absolute path to the jar and even browsing to the folder which contains the jar. No luck whatsoever.
I have tried using '/' instead of '.' in the package name. No luck there either.
I have tried using java VM arguments providing huge enough heap space. Nada.
Tried executing on powershell. Got the below error:
+ CategoryInfo : NotSpecified: (Error: Could not lo...ClassA:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
I wanted to see the contents directly so used jar -tvf path_to_jar\sample.jar . Interestingly jar command didn't execute and complained that command was not found on the classpath. I had to browse to the java installation directory and execute the command and it showed the file contents.
I am running out of ideas and options here.
Any pointers?
For some reason, no matter which config I use for fatJar, the final jar gives errors when trying to execute the main class.
Using ShadowJar task I was able to build the necessary jar and execute the main class as specified in the question.
I know this defeats the entire purpose of the question about using fatJar and executing the main class. But due to time-constraints, I had to look for alternatives.
For those who are looking for config for Shadow Jar tasks, I did it using below:
buildscript {
repositories {
mavenLocal()
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3'
}
}
}
apply plugin: 'com.github.johnrengelman.shadow'
and finally to execute the task, I used
gradle shadowJar
It looks like your execution command is not valid. After building your project with:
./gradlew clean fatJar
you should use following command to execute your program:
java -cp build/libs/xxxxxxx.jar com.example.ClassA
Parameter -cp build/libs/xxxxxxx.jar sets the classpath for JVM that is about to start and it has to point to the existing JAR file (you can use relative or absolute path to this file).
Take a look at following screencast I've recorded a few minutes ago:
You can see that if I change execution command to:
java -cp xxxxxxx.jar com.example.ClassA
I got exactly the same error as yours:
Error: Could not find or load main class com.example.ClassA
Here you can find a simple Gradle project I used to play around with your build.gradle file example. I've added a single dependency to prove that fatJar builds a JAR with dependencies and I've added to classes with public static void main(String[] args) to prove that you can pick any main class from command line. I hope it helps.

How to specify "sources" JAR for local JAR dependency?

I have a *.jar file in my Gradle / Buildship project that resides in a lib folder. I include it in my build.gradle via:
compile files('libs/local-lib.jar')
I also have a correspondinglocal-lib-sources.jar file that I would like to attach to it. In Eclipse, for manually managed dependencies this works via context menu of build path entry -> Properties -> Java Source Attachment. However, for gradle-managed dependencies, that option is not available.
Does anybody know how what the gradle/buildship way to do this looks like? My dependency is in no repository, so I'm stuck with compile files for now.
If you want to use Buildship with Eclipse then you are out of luck since this is not currently supported by gradle (see https://discuss.gradle.org/t/add-sources-manually-for-a-dependency-which-lacks-of-them/11456/8).
If you are ok with not using Buildship and manually generating the Eclipse dot files you can do something like this in your build.gradle:
apply plugin: 'eclipse'
eclipse.classpath.file {
withXml {
xml ->
def node = xml.asNode()
node.classpathentry.forEach {
if(it.#kind == 'lib') {
def sourcePath = it.#path.replace('.jar', '-sources.jar')
if(file(sourcePath).exists()) {
it.#sourcepath = sourcePath
}
}
}
}
}
You would then run gradle eclipse from the command line and import the project into Eclipse using Import -> "Existing Projects into Workspace"
Another (possibly better) option would be to use a flat file repository like this:
repositories {
flatDir {
dirs 'lib'
}
see https://docs.gradle.org/current/userguide/dependency_management.html#sec:flat_dir_resolver
You would then just include your dependency like any other; in your case:
compile ':local-lib'
This way Buildship will automatically find the -sources.jar files since flatDir acts like a regular repository for the most part.
Use an extra folder called lib or similar on the same directory level as src or you build script.
dependencies {
//local file
compile files('lib/local-lib-sources.jar')
// others local or remote file
}

Gradle Multi Module jar

I have the following project config (pure java):
/
/Base_module
/A_module
A_module depends on Base_module.
I want a .jar containing the A_module classes + the Base_module classes, but can't make it.
With the following config, I can only achieve different jars for each module:
settings.gradle
include 'Base_module', 'A_module'
build.gradle
....
project(':Base_module') {
}
project(':A_module') {
dependencies {
compile project(':Base_module')
}
}
....
What do I need to add to achieve the full .jar?
Thanks in advance.
You can try to make a custom task of Jar type in your root build script, which will include all the classes of all subprojects or just a number of subprojects. It could be something similar to:
//declare an array, containing subproject names, which classes you want to collect
def projectsToCollect = [':Base_Module',':A_Module']
//create a custom task, which assembles an jar-archive and depends on subproject compilation tasks,
//that causes sibprojects sources been compiled before thist task runs
task singleJar( type: Jar , dependsOn: projectsToCollect.collect{ it+":compileJava"}) {
//set new jar name
baseName = 'singleJar'
//set files, which will be included in this new jar
from files(projectsToCollect.collect{ project(it).sourceSets.main.output })
}
You can play it around, modifying sourcesets, if you have some custom in your subprojects, or if you want to add tests.
That would be fatJar aka uberJar aka shadowJar
take a look at https://github.com/johnrengelman/shadow
You would need to configure for you ':A_module' project

Basic gradle Q for multiple projects

This is a basic gradle question but I could not find an answer:
If I have multiple independent projects that share the same root directory, how should I use different build.gradle file for them?
for example the directory structure is like this:
src/main/java/package1/base1/project1package/
src/main/java/package1/base1/project2package/
It looks like the build.gradle file should be placed at root, but how to differ two projects which are totally independent and not related to each other? When I use gradle build command, how can I specify one project to be built? Thanks.
To be honest I don't like the structure you are proposing. The standard one looks like this:
<root>/build.gradle
<root>/<subproject1>/build.gradle
<root>/<subproject1>/src/main/java/<basepackage>/<subproject1>/
<root>/<subproject2>/build.gradle
<root>/<subproject2>/src/main/java/<basepackage>/<subproject2>/
The subproject1 and subproject2 are independent of each other. Moreover:
<root>/build.gradle can contain a build configuration, tasks and so on common to all sub-projects. subproject1-specific stuff is then in <root>/<subproject1>/build.gradle
If you execute gradle build in <root>/, all sub-projects will be built. If you do this in <root>/<subproject1>/, only the subproject1 will be built.
For more, see http://www.gradle.org/docs/current/userguide/multi_project_builds.html
Let me know if I am off base, but essentially what I think you want to do is simply produce two separate JARs from two sets of source that happen to live in the same root directory. This is a pretty funny way to structure your project, as #Tomas mentioned, but assuming you cannot change your project structure, you could simply create custom source sets to solve this problem.
Your build could look something like this.
apply plugin: 'java-base'
sourceSets {
projA {
java {
srcDir 'src/main/java'
include 'pkg1/**'
}
}
projB {
java {
srcDir 'src/main/java'
include 'pkg2/**'
}
}
}
task projAJar(type: Jar) {
baseName 'projA'
from sourceSets.projA.output
}
task projBJar(type: Jar) {
baseName 'projB'
from sourceSets.projB.output
}
Although this is technically one Gradle project, these two source sets each have their own compile tasks, jar tasks, and configurations. You would configure your dependencies like so.
dependencies {
projACompile 'foo.org:lib:1.0'
projBCompile 'bar.org:lib:2.0'
}

Categories