Gradle circular dependency, but not seeing it - java

So I have a "main" module called main-data. It doesn't depend on any of my other projects.
main-data build.gradle
dependencies {
implementation("org.postgresql:postgresql:${Versions.postgresVersion}")
implementation("com.zaxxer:HikariCP:${Versions.hikariVersion}")
}
// To run: ./gradlew flywayMigrate
flyway {
url = "jdbc:postgresql://localhost:5432/app"
user = "app"
password = "app"
validateOnMigrate = false
}
Then I have another project vendor-data that depends on main-data.
dependencies {
implementation(project(":app-main-data"))
}
And finally I have another module invoice-data that depends on both.
dependencies {
implementation(project(":app-main-data"))
implementation(project(":app-modules:vendors:data"))
}
And the error:
FAILURE: Build failed with an exception.
* What went wrong:
Circular dependency between the following tasks:
:app-modules:invoices:data:compileJava
+--- :app-modules:invoices:data:compileJava (*)
\--- :app-modules:invoices:data:compileKotlin
+--- :app-modules:invoices:data:compileJava (*)
\--- :app-modules:invoices:data:compileKotlin (*)
(*) - details omitted (listed previously)
My settings.gradle
include(
":app-main",
":app-main-data",
)
include(
":app-modules:vendors:data",
)
include(
":app-modules:invoices:api",
":app-modules:invoices:data",
)
Where is the circular dependency here? As soon as I remove implementation(project(":app-modules:vendors:data")) from invoice project it works. But I'm puzzled.
Can anyone understand this?

Related

How to write Gradle plugin to cut down build.gradle file

A bit of Context first:
I am working on migrating my companies projects to be built by Gradle.
One thing, that this results in, is having redundancy in my build.gradle files,
as I am configuring the same Skeleton over and over again.
This includes:
Setting the java-,maven-publish- and org.sonarcube-plugin
Configuring the repositories to be mavenCentral and our private Artifactory Repo
Configuring the publishing block, that is all the same, except for the artifactId
Building a Manifest inside the Jar block (using helper Methods, to correctly build the Manifests classpath)
Helper Methods
two Tasks
two dependsOn statements
build.gradle file as of now:
plugins {
id 'io.spring.dependency-management' version '1.0.12.RELEASE'
id "org.sonarqube" version "3.2.0"
id 'maven-publish'
id 'java'
}
group = 'group'
version = 'version'
sourceCompatibility = '11'
ext.artifactName = 'ProjectName'
// Where to look for dependencies:
repositories {
mavenCentral()
maven{
credentials{
username = "${artifactory_user}"
password = "${artifactory_password}"
}
url "${artifactory_contextUrl}"
allowInsecureProtocol = true
}
}
// Where to publish what Artifacts to:
publishing {
publications {
maven(MavenPublication) {
groupId = 'group'
artifactId = 'ProjectName'
String buildEnvVar = System.env.BUILDENVIRONMENT
if(buildEnvVar == null){
version = 'LOCAL BUILD'
}else{
version = 'version'
}
from components.java
}
}
repositories {
maven {
// change to point to your repo, e.g. http://my.org/repo
name = "gradle-dev"
url = "${artifactory_contextUrl}"
allowInsecureProtocol = true
credentials{
username = "${artifactory_user}"
password = "${artifactory_password}"
}
}
}
}
dependencies {...}
jar {
// configuration of variables
String dateString = new Date().format("yyyy-MM-dd HH:mm:ss ")
String localBuild = "LOCAL BUILD by " + System.getProperty("user.name") + " on " + dateString
String buildEnvVar = System.env.BUILDENVIRONMENT
String buildEnvironment
String classpath = createCP()
if(buildEnvVar == null){
buildEnvironment = localBuild
archiveName = "ProjectName"
}else{
buildEnvironment = buildEnvVar
archiveFileName= "ProjectName_" + version + ".jar"
delete fileTree("build/libs") {
include('*')
}
}
manifest {
attributes (
"Main-Class": "org.example.foo",
"Specification-Title" : "ProjectName",
"Specification-Vendor" : "blab",
"Specification-Version" : "Spec-version",
"Implementation-Title" : "$System.env.JOB_NAME",
"Implementation-Version" : "Impl-version",
"Implementation-Vendor" : "blub",
"Implementation-Vendor-Id" : "blob",
"Implementation-Url" : "bleb",
"Build-By" : buildEnvironment,
'Class-Path': classpath
)
}
}
String createCP () {
// super secret can't share
}
// will suffix the jars with release or debug, depending on it being compiled with or without debug-information
project.gradle.taskGraph.whenReady{
boolean isDebug = project.gradle.taskGraph.getAllTasks().join(' ').contains('debugJar')
compileJava.options.debug = isDebug
String suffix = isDebug? "debug" : "release"
String fullJarName = "$artifactName-$suffix" + ".jar"
jar.setProperty('archiveName', fullJarName)
}
tasks.named('test') {
useJUnitPlatform()
}
task debugJar() {}
debugJar.dependsOn(jar)
//Downloads all Jars the project depends on, and saves them in buildDirectory/output/libs if the gradle build command is executed.
task copyToLib(type: Copy) {
into "${buildDir}/output/libs"
from configurations.runtimeClasspath
}
build.dependsOn(copyToLib)
what I want to achive:
plugins {
id 'io.spring.dependency-management' version '1.0.12.RELEASE'
id "org.sonarqube" version "3.2.0"
id 'maven-publish'
id 'java'
id 'mySuperPlugin'
}
// Configure mySuperPlugin
mySuperPlugin {
artifactId = 'xyz'
mainClass = 'org.example.foo'
version = 'version'
stuffFromOtherTasks = ...
}
// Where to look for dependencies:
repositories {
mavenCentral()
maven{
credentials{
username = "${artifactory_user}"
password = "${artifactory_password}"
}
url "${artifactory_contextUrl}"
allowInsecureProtocol = true
}
}
dependencies {...}
Most of the values are the same.
The ones that aren't are passed in via Environment-Variables (Jenkins-JobName,...),
or get determined through helper Methods.
I reckon, that i will most likely not end up with a buildfile like the one above,
but atleast some of the buildfile must be outsourceable.
I know as of now, that i can create seperate Tasks in a plugin, like comparing two files, that have been passed. What I didn't find a solution to yet:
Can I modify the Jar Task of the project applying the plugin, inside the plugin?
How do I pass Outputs from other Tasks into my plugins tasks?
How do I access the applying projects data (i.e. the runtimeClasspath)
Is a plugin even what i want to do, or is there another way of cutting down the build.gradle file?
I am relatively unexperienced with gradle. I have read through quite a bit of the docs and other postings, but chances are i just overlooked some best-practice way of doing certain things.
Therefore, feel free to criticize my buildfile aswell as my approach!
You can do this in a couple of ways. And this comes down to if your project is composed of multiple sub-projects or if they are stand alone projects. For stand alone projects you can do the following in your settings.gradle file:
includeBuild("../common-project/build.gradle")
The common project would just house the common build.gradle file, and you'd declare all of the items you want to share in there. It would look like a normal build.gradle file.
That would require that each project share the common configuration from another project, but wouldn't require any additional projects be checked out. Just the common project and the project itself. For more details see:
https://docs.gradle.org/current/userguide/composite_builds.html
If you have more of a multi-project build like say many micro-services or subprojects that make up a larger project then using multi-project setup and declare the common pieces in the allprojects closure in the root build.gradle:
allprojects {
plugin: java
plugin: web
repositories {
....
}
dependencies {
....
}
}
In the multi-project case you'd have to check out the top level project and all subprojects. Your folder layout might look like this:
- my-project
- service-1
src
build.gradle
- service-2
src
build.gradle
- service-3
src
build.gradle
build.gradle
settings.gradle
In the multi-project setup service1, service2, and service3 would be declared in the settings.gradle file using include like so:
rootProject.name = 'my-project'
include `service1`
include `service2`
include `service3`
In a multi-project setup you'd typically house that in a single source repository as oppose to using includeBuild where each project would belong to a separate source code repo. The former way forces you to checkout the appropriate number of projects in the multi-project case. But, in the includeBuild case the developer would have to know to check out minimum of 2 projects.

Running jar results in ClassNotFoundException (Gradle)

So I'm relatively inexperienced on how gradle works, and I need some help getting my jar working. My application generates some files through the terminal. However, when I try to run the jar, it gives me an error.
build.gradle:
plugins {
id 'java'
}
group 'me.tl0x'
version '1.0'
repositories {
mavenCentral()
}
jar {
manifest {
attributes (
'Main-Class': 'me.tl0x.Main'
)
}
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation 'org.freemarker:freemarker:2.3.29'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'org.jline:jline:3.21.0'
implementation 'org.fusesource.jansi:jansi:2.4.0'
}
test {
useJUnitPlatform()
}
Error Message:
PS C:\Path> java -jar ./build/libs/FabricModGenerator-1.0.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/jline/terminal/TerminalBuilder
at me.tl0x.terminal.Interface.<init>(Interface.java:29)
at me.tl0x.Main.main(Main.java:48)
Caused by: java.lang.ClassNotFoundException: org.jline.terminal.TerminalBuilder
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:636)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:182)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:519)
... 2 more
Any help would be greatly appreciated.
It's basically because your classpath doesn't include "jline" and the other stuffs required by your application.
Just use the application plugin and let that do the right thing (this is the right answer...)
But if it's a utility thing that you're doing...
Create a task that does the right thing and just run it via gradle (since you have no arguments)
def asFileUrl(filepath) {
return "file:///" + new java.net.URI(null, filepath, null).toASCIIString();
}
task LauncherJar(type: Jar) {
appendix = "launcher"
ext.launcherClasspath = { ->
def verifyLibs = [
configurations.runtimeClasspath.collect { asFileUrl(it.getCanonicalPath()) }.join(' '),
asFileUrl(jar.archivePath.getCanonicalPath())
]
return verifyLibs.join(' ')
}
manifest {
attributes ("Class-Path": launcherClasspath())
}
}
task execMyJar(type: JavaExec, dependsOn: [jar, LauncherJar]) {
group = 'Execution'
description = 'Do The thing that needs doing'
classpath = files(LauncherJar.archivePath)
mainClass = 'me.tl0x.Main'
}
Then you can just do gradle execMyJar.
Note that here, I'm creating a launcher jar with the Class-Path element in the Manifest file. The reason for that is to avoid the situation (Windows only?) where your command line is too long... It might not matter in your case since you haven't got that many dependencies listed (but I don't know about transitive dependencies).

Get module path not root path in Java

I have a multi-module Java(Spring) project, which build by Gradle 6.7.1. And I use in Jetbrain IDEA to develop. The file Structure like this:
root
|--orm
| +---hibernates
|
|--web
|--mvc
|--rest
And then, I have tried some codes in my module project like below, what I get all are root path (/home/user/IdeaProjects/root/), not module path (/home/user/IdeaProjects/root/web/mvc). How can I get module path (/home/user/IdeaProjects/root/web/mvc) ?
new File("").getAbsolutePath()
Assuming for instance that your mvc project is setup like this in setting.gradle, in the root folder :
include 'mvc'
project(':mvc').projectDir = new File('./web/mvc')
Then, to get the path /home/user/IdeaProjects/root/web/mvc, just try this :
println project(':mvc').projectDir
Will prints :
/home/user/IdeaProjects/root/web/mvc
based on the answer of #ToYonos. We can do that by this:
settings.gradle gets the project path of every module.
write a key value into the info.properties in every module.
Spring Project read this properties file.
Code
Because struct of my project is:
root
|--orm
| +---mybatis
| +---jpa
| +---...
|--web
+--mvc
+--rest
+--...
So, I should loop twice to get the module name. And I exclude project without build.gradle.
file("${rootDir}").eachDir {
it.eachDirMatch(~/.*/) {
if (it.list().contains("build.gradle")) {
def moduleName = "${it.parentFile.name}:${it.name}"
println " ${moduleName}"
include moduleName
}}}
And then, read and write info.properties.
import java.nio.file.Paths
// read
def project_dir = project(":${moduleName}").projectDir
def propFile = Paths.get("${project_dir}", "src", "main","resources","info.properties").toFile()
propFile.createNewFile()
Properties props = new Properties()
propFile.withInputStream {
props.load(it)
}
// write
props.setProperty("project.dir","$project_dir")
props.store propFile.newWriter(), null

Gradle SubProject tasks are not getting picked-up

I have a multi-module setup for a Java project with following structure.
mainApp
|--> core-module
| |--> src
| |--> build.gradle
| |--> gradle.properties
|
|--> lib-module
| |--> src
| |--> build.gradle
| |--> gradle.properties
|--> lib-another-module
| |--> src
| |--> build.gradle
| |--> gradle.properties
|--> settings.gradle
|--> build.gradle
in mainApp/build.gradle I've mentioned
subprojects {
test.dependsOn "CreateMessageKeys"
//test.dependsOn ":CreateMessageKeys"
//test.dependsOn ("CreateMessageKeys")
//test.dependsOn (":CreateMessageKeys") none of this working....
}
task CreateMessageKeys(type: CreateMessageKeysTask) {
destDir = "bundle-common/src/";
outputClass = "common.messages.MessageKeys";
}
and my core-module/build.gradle have a test target as
test {
useTestNG() {
useDefaultListeners = true
suites 'test/testng.xml'
}
}
but getting error as below.. What Am I missing here?
Caused by: groovy.lang.MissingMethodException: No signature of method: java.lang.String.dependsOn() is applicable for argument types: (String) values: [CreateMessageKeys]
Possible solutions: respondsTo(java.lang.String)
Edit
subprojects {
test.dependsOn(rootProject.tasks['CreateMessageKeys'])
}
task CreateMessageKeys(type: CreateMessageKeysTask) {
destDir = "bundle-common/src/";
outputClass = "common.messages.MessageKeys";
}
It generates the error:
* What went wrong:
A problem occurred evaluating root project 'myApp'.
> Task with name 'CreateMessageKeys' not found in root project 'mainApp'.
The task definition does not look correct, see defining tasks.
It should look like this:
task ('CreateMessageKeys', type: CreateMessageKeysTask) {
destDir = "bundle-common/src/";
outputClass = "common.messages.MessageKeys";
}
or
task (CreateMessageKeys, type: CreateMessageKeysTask) {
destDir = "bundle-common/src/";
outputClass = "common.messages.MessageKeys";
}
Irrelevant to this question:
In your second example, perhaps the task CreateMessageKeys does not exist yet when this is evaluated. One possible workaround would be the following:
subprojects.each {
it.afterEvaluate {
it.test.dependsOn(...)
}
}
Or simply putting the task definition above this block could resolve this.

geotools 20.5 error: Provider org.geotools.referencing.factory.epsg.CartesianAuthorityFactory could not be instantiated

I am working on a java project which required to convert WGS84 to UMT. I used geotools v20.5 to create a transform with following code:
transform = CRS.findMathTransform(
CRS.decode("EPSG:4326", true),
CRS.decode("EPSG:3857", true),
false);
It was working correctly until geotools changed their repos.
Currently when I run the program, I will get a warning:
WARNING: Can't load a service for category "CRSAuthorityFactory". Cause is "ServiceConfigurationError: org.opengis.referencing.crs.CRSAuthorityFactory: Provider org.geotools.referencing.factory.epsg.CartesianAuthorityFactory could not be instantiated".
then with following errors:
Caused by: java.lang.NoSuchFieldError: ONE
Execution failed for task ':Application.main()'.
> Process 'command '/usr/lib/jvm/java-8-openjdk-amd64/bin/java'' finished with non-zero exit value 255
my environment: Ubuntu 20.04 with OpenJDK 8 (64bit)
I tested with other Windows machine, it hits same error.
Here are the libs I am using from org.geotools
def geotoolsVersion=20.5
compile group: 'org.geotools', name: 'gt-opengis', version: geotoolsVersion
compile group: 'org.geotools', name: 'gt-referencing', version: geotoolsVersion
compile group: 'org.geotools', name: 'gt-epsg-wkt', version: geotoolsVersion
compile group: 'org.geotools', name: 'gt-geometry', version: geotoolsVersion
As you may notice I am using gt-epsg-wkt instead of those ones with db since I may not have write permission to some directories in production. But I tested other plugins with db, still hitted same error.
I tried to debug which part of code in geotools caused that error and I found out.
The error started at the last line Units.autoCorrect(...) of following codes in Parser.java class in gt-referencing lib.
/**
* Parses an "UNIT" element. This element has the following pattern:
*
* <blockquote>
*
* <code>
* UNIT["<name>", <conversion factor> {,<authority>}]
* </code>
*
* </blockquote>
*
* #param parent The parent element.
* #param unit The contextual unit. Usually {#link SI#METRE} or {#link SI#RADIAN}.
* #return The "UNIT" element as an {#link Unit} object.
* #throws ParseException if the "UNIT" can't be parsed.
* #todo Authority code is currently ignored. We may consider to create a subclass of {#link
* Unit} which implements {#link IdentifiedObject} in a future version.
*/
#SuppressWarnings("unchecked")
private <T extends Quantity<T>> Unit<T> parseUnit(final Element parent, final Unit<T> unit)
throws ParseException {
final Element element = parent.pullElement("UNIT");
final String name = element.pullString("name");
final double factor = element.pullDouble("factor");
final Map<String, ?> properties = parseAuthority(element, name);
element.close();
Unit<T> finalUnit = (factor != 1) ? unit.multiply(factor) : unit;
return Units.autoCorrect(finalUnit);
}
Then I stepped into it and found out following method in DefaultUnitParser.java class in gt-referencing lib
DefaultUnitParser.getInstance()
//this method returns null, with error said org.geotools.measure.units failed to instantialize.
I am totally lost now, why it was working and sunddly not working after they changed their remote repo?!
If you need further info from me, please leave a comment and I am still awaiting for a solution since I cannot easily change geotools lib.
Thanks all
BTW I confirmed it gets correct WKT via code: EPSG:4326 or EPSG:3857
UPDATE
I changed the geotools version down to 12.5 which is written on their website https://geotools.org/about.html and switched JTS lib to com.vividsolutions.jts then it is working now. I think I may need to raise an issue on their github.
The trick to solving this sort of issue is to see what gradle is pulling in as dependecies using gradle -q dependencies - if you have a well constructed build you should see something like:
+--- org.locationtech.jts:jts-core:1.16.1
+--- org.geotools:gt-opengis:23.1
| +--- commons-pool:commons-pool:1.5.4
| +--- systems.uom:systems-common-java8:0.7.2
| | +--- tec.uom:uom-se:1.0.8
| | | +--- javax.measure:unit-api:1.0
| | | \--- tec.uom.lib:uom-lib-common:1.0.2
| | | \--- javax.measure:unit-api:1.0
| | +--- si.uom:si-quantity:0.7.1
| | | \--- javax.measure:unit-api:1.0
| | \--- si.uom:si-units-java8:0.7.1
| | +--- javax.measure:unit-api:1.0
| | +--- tec.uom:uom-se:1.0.8 (*)
| | \--- si.uom:si-quantity:0.7.1 (*)
| \--- javax.media:jai_core:1.1.3
+--- org.geotools:gt-epsg-wkt:23.1
| +--- org.geotools:gt-referencing:23.1
| | +--- org.ejml:ejml-ddense:0.34
| | | \--- org.ejml:ejml-core:0.34
| | +--- commons-pool:commons-pool:1.5.4
| | +--- org.geotools:gt-metadata:23.1
If however you see something like:
+--- org.locationtech.jts:jts-core:1.16.1
+--- org.geotools:gt-opengis:23.1
+--- org.geotools:gt-epsg-wkt:23.1
+--- org.geotools:gt-geometry:23.1
+--- org.geotools:gt-referencing:23.1
+--- org.geotools:gt-main:23.1
\--- org.geotools:gt-metadata:23.1
then something is wrong. From my (brief) experiments with gradle it seems that you need something like:
repositories {
maven { url "http://download.java.net/maven/2" }
maven { url "https://repo.osgeo.org/repository/release/" }
maven { url "http://maven.geo-solutions.it/" }
maven { url "https://repo.maven.apache.org/maven2" }
}
to make sure that gradle can find all the dependencies that you (and GeoTools) needs. Note if you are using snapshots of GeoTools you will need https://repo.osgeo.org/repository/snapshot/ instead of https://repo.osgeo.org/repository/release/.

Categories