I'm working on a Spring Boot app with multiple modules and we're using Gradle to build it. Unfortunately I can't get the Gradle configuration right.
The project structure is this:
parent
|
+ build.gradle
|
+ settings.gradle
|
+ core
| |
| + build.gradle
|
+ apis
| |
| + build.gradle
|
+ services
| |
| + build.gradle
|
+ data
| |
| + build.gradle
When I try to build the project, I get compilation errors like error: cannot find symbol saying, that the classes from data used in services aren't imported. And I assume this is true between all the modules.
My parent build.gradle looks like this:
buildscript {
ext {
springBootVersion = '2.0.0.BUILD-SNAPSHOT'
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'eclipse'
apply plugin: 'idea'
allprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
testCompile('org.springframework.boot:spring-boot-starter-test')
}
}
dependencies {
compile project(':data')
compile project(':services')
compile project(':apis')
compile project(':core')
}
jar {
baseName = 'my-jar'
version = '0.0.1-SNAPSHOT'
}
settings.gradle:
rootProject.name = 'my-app'
include ':apis'
include ':core'
include ':data'
include ':services'
one of children (core) has this in it's build.gradle:
dependencies {
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-starter-quartz')
compile('org.springframework.boot:spring-boot-starter-tomcat')
runtime('com.h2database:h2')
compile project(':data')
compile project(':services')
compile project(':apis')
}
services build.gradle looks like this:
dependencies {
compile('org.springframework.boot:spring-boot-starter-quartz')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
runtime('com.h2database:h2')
compile project(':data')
}
The other ones also declare only dependencies.
The dependency structure looks like this:
core - apis, services, data
apis - services, data
services - data
data -
Example of the compilation errors:
/my-app/services/src/main/java/com/example/my-app/business/IssuedTokenService.java:3: error: package com.examples.data.dao does not exist import com.examples.data.dao.IssuedToken;
/my-app/services/src/main/java/com/example/my-app/business/IssuedTokenService.java:4: error: package com.examples.data.repository does not exist import com.examples.data.repository.IssuedTokenRepository;
/my-app/services/src/main/java/com/example/my-app/business/IssuedTokenService.java:12: error: cannot find symbol
private IssuedTokenRepository issuedTokenRepository;
symbol: class IssuedTokenRepository
location: class IssuedTokenService
/my-app/services/src/main/java/com/example/my-app/business/IssuedTokenService.java:15: error: cannot find symbol
public void saveToken(IssuedToken issuedToken) {
^
symbol: class IssuedToken
location: class IssuedTokenService
In your utility (data) projects put:
bootJar {
enabled = false
}
jar {
enabled = true
}
If kotlin dsl
tasks.getByName<BootJar>("bootJar") {
enabled = false
}
tasks.getByName<Jar>("jar") {
enabled = true
}
The answer is similar to this one.
Gradle documentation on multi-project builds states:
A “lib” dependency is a special form of an execution dependency. It
causes the other project to be built first and adds the jar with the
classes of the other project to the classpath.
A repackaged jar, thus, poses a problem.
If, say, project B depends on project A, and the project A has a org.springframework.boot plugin applied to it, a simple compile project(':A') dependency will not work because the project jar is repackaged during the bootRepackage or bootJar task. The resulting fat jar has different structure, preventing Gradle from loading its classes.
In this case, the dependencies should be written the following way:
dependencies {
compile project(':A').sourceSets.main.output
}
Using output of a source set directly is equivalent to using the "normal" resulting jar of project A before it is repackaged.
bootJar {
enabled = false
}
jar {
enabled = true
}
This works for my. In my case use spring boot with spring cloud with multi project and gradle fail to build. With this solution works!
You should exclude org.springframework.boot from all submodules (root build.gradle file, allProjects block), that are stands as dependencies for your core module, which will be build to fat-jar.
Include dependencyManagement configuration into all of your spring-boot managed submodules:
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-starter-parent:${springBootVersion}"
}
}
The reason of your problem is the absence of submodules compiled jar files inside your core module fat-jar.
Related
With the following gradle file within a multi project or multi module build, (lets call it web)
plugins {
id 'groovy'
id 'org.springframework.boot'
id 'io.spring.dependency-management'
}
dependencies {
implementation "org.codehaus.groovy:groovy:2.5.14"
implementation(project(':persistence'))
}
I'm unable to resolve the class files present in the project 'persistence' and get the following error while compiling
unable to resolve class XXX
This is the gradle file of the 'persistence' module
plugins {
id 'groovy'
id 'org.springframework.boot'
id 'io.spring.dependency-management'
}
dependencies {
implementation "org.codehaus.groovy:groovy:2.5.14"
}
What could be the problem?
Also, here is the settings.gradle and directory structure
// settings.gradle
pluginManagement {
plugins {
id 'org.springframework.boot' version "2.3.3.RELEASE"
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
rootProject.name = 'gradle-bug'
include 'commons'
include 'persistence'
include 'security'
include 'web'
When gradle sees a "implementation" dependency on a project, it uses the jar of that dependency for compilation.
When you add the org.springframework.boot plugin, it by default disables the jar creation task for that module, but gradle continues to depend on that jar for its compilation, even though the classes of that module have been generated in the build/classes folder.
To fix this, you have to enable the jar creation by adding the following snippet to the persistence module
jar {
enabled = true
}
I am migrating our product's build from Ant to Gradle, having started from a single project and got the following error:
> Could not resolve all files for configuration ':Shared:serverbase:compileClasspath'.
> Could not find guava:guava:23.3-jre.
Searched in the following locations:
- https://jcenter.bintray.com/guava/guava/23.3-jre/guava-23.3-jre.pom
- file:/F:/pros/X/java/guava/guava-23.3-jre.xml
Required by:
project :Shared:serverbase
Why it is looking for xml-files instead of jar?
Here are my files:
build.gradle in project's root directory:
buildscript {
repositories {
jcenter()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'de.richsource.gradle.plugins:gwt-gradle-plugin:0.6' // gwt compiler plugin
}
}
allprojects {
apply from: rootProject.file('libraries.gradle')
repositories {
jcenter()
ivy {
url "file://${rootProject.projectDir}/ThirdParty/java/"
patternLayout {
artifact "[organization]/[module](-[revision])(.[ext])"
}
}
}
}
subprojects {
apply plugin: 'java-library'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.debugOptions.debugLevel = "lines,vars,source"
compileJava.options.compilerArgs += ["-nowarn"]
compileJava.options.debug = true
}
build.gradle of this single project:
sourceSets.main.java.srcDirs = ['src']
dependencies {
implementation "guava:guava:${guavaVersion}"
implementation "slf4j:jul-to-slf4j:${slf4jVersion}"
implementation "logback:logback-classic:${logbackVersion}"
}
jar {
manifest {
attributes 'Class-Path': configurations.compileClasspath.collect { it.getName() }.join(' ')
}
}
settings.gradle:
rootProject.name = 'X'
include 'Shared:serverbase'
libraries.gradle:
ext {
...
guavaVersion = '23.3-jre'
...
}
(some content stripped)
And if I add file dependency to build.gradle as local file (How to add local .jar file dependency to build.gradle file?)
implementation files("guava-${guavaVersion}.jar")
I got tons of errors like 'error: package org.slf4j does not exist' so it seems that dependencies were not satisfied at all.
The outcome should be a single jar with compiled sources.
I am a novice in gradle, please forgive my unenlightenment.
Your Guava dependency is not correct.
Change from:
implementation "guava:guava:${guavaVersion}"
To:
implementation "com.google.guava:guava:${guavaVersion}"
In order to not manually manage, both, Maven and Gradle build configuration files, I wanted to let Gradle generate the Maven POMs for a multi-module build.
This is working so far. Below you find the settings.gradle
rootProject.name = 'parent'
include 'module-a'
include 'module-b'
and here follows build.gradle
allprojects {
apply plugin: 'maven'
group = 'com.example.project'
version = '1.0-SNAPSHOT'
}
subprojects {
apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
task createPom << {
pom {
project {
parent {
groupId project.group
artifactId rootProject.name
version project.version
}
}
}.writeTo("pom.xml")
}
}
task createPom << {
pom {
project {
packaging 'pom'
modules {
module 'module-a'
module 'modula-b'
}
}
}.writeTo("pom.xml")
}
The problem is that I have to manually declare the modules in the createPom task of the root project. Also, I need two dedicated tasks; one for the root project and and for the subprojects.
How can I let Gradle figure out what the modules are? Or is there a way to programmatically determine and add the subprojects as modules? Furthermore, is it even necessary to have two distinct tasks?
I am trying to use Google checkstyle configuration (https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml) but I am constantly getting an error on gradle check:
Unable to create a Checker: cannot initialize module TreeWalker - Unable to instantiate EmptyCatchBlock
I used Gradle to build the project. Below is my gradle.build.
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'checkstyle'
sourceCompatibility = 1.8
version = '1.0'
checkstyle {
toolVersion = "6.3"
}
task "create-dirs" << {
sourceSets*.java.srcDirs*.each { it.mkdirs() }
sourceSets*.resources.srcDirs*.each { it.mkdirs() }
}
jar {
manifest {
attributes 'Implementation-Title': 'xyz',
'Implementation-Version': 0.01
}
}
repositories {
mavenCentral()
}
dependencies {
compile (
['org.apache.logging.log4j:log4j-api:2.2'],
['org.apache.logging.log4j:log4j-core:2.2']
)
testCompile(
['junit:junit:4.11'],
['org.mockito:mockito-core:1.+']
)
}
test {
systemProperties 'property': 'value'
}
uploadArchives {
repositories {
flatDir {
dirs 'repos'
}
}
}
Also, when I try to add XML config file to Checkstyle plugin in IDEA I get similar error but with a stack trace:
org.infernus.idea.checkstyle.exception.CheckStylePluginException: <html><b>The CheckStyle rules file could not be loaded.</b><br>cannot initialize module TreeWalker - Unable to instantiate EmptyCatchBlock</html>
at org.infernus.idea.checkstyle.checker.CheckerFactory.blacklistAndShowMessage(CheckerFactory.java:234)
at org.infernus.idea.checkstyle.checker.CheckerFactory.createChecker(CheckerFactory.java:188)
at org.infernus.idea.checkstyle.checker.CheckerFactory.getOrCreateCachedChecker(CheckerFactory.java:98)
at org.infernus.idea.checkstyle.checker.CheckerFactory.getChecker(CheckerFactory.java:73)
at org.infernus.idea.checkstyle.checker.CheckerFactory.getChecker(CheckerFactory.java:41)
I cannot figure out what am I doing wrong. Any help would be appreciated.
Gradle version: 2.2
You can add this configuration into your build.gradle file:
configurations {
checkstyleOverride
}
dependencies {
checkstyleOverride('com.puppycrawl.tools:checkstyle:6.11.2')
}
tasks.withType(Checkstyle) {
checkstyleClasspath = project.configurations.checkstyleOverride
}
Enjoy!
The problem lies in the fact that com.puppycrawl.tools.checkstyle.checks.blocks.EmptyCatchBlockCheck was indeed added to checkstyle but for version 6.4-SNAPSHOT. As it can be seen in checkstyle repository (pom.xml history) version 6.4-SNAPSHOT was introduced on the 02.02.2015 and EmptyCatchBlockCheck class was created on 18.02.2015.
Gradle still uses version 6.3 as in the following log extract:
:checkstyleMain
Download https://repo1.maven.org/maven2/com/puppycrawl/tools/checkstyle/6.3/checkstyle-6.3.pom
So there's simply no class You'd like to use.
According to the docs checkstyle classpath can be specified with checkstyleClasspath property - you can try to set it up manually.
I've also prepared a demo with 6.4-SNAPSHOT version, it can be found here. Checkstyle jar was built with mvn clean package with source taken from this repo.
Here is an approach that works with the (currently) latest versions of Gradle & Checkstyle (Gradle 6.1.1 & Checkstyle 8.29):
plugins {
id 'java'
id 'checkstyle'
}
configurations {
checkstyleConfig
}
dependencies {
checkstyleConfig("com.puppycrawl.tools:checkstyle:8.29") { transitive = false }
}
checkstyle {
toolVersion '8.29'
config = resources.text.fromArchiveEntry(configurations.checkstyleConfig, 'google_checks.xml')
}
Note that the Checkstyle dependency excludes transitive dependencies, otherwise the resources.text.fromArchiveEntry will fail since multiple JAR files will be present, and it will be unable to select a single one.
I'm facing some problems with my multimodule project gradle build in IntelliJ. My gradle build works correctly, but IntelliJ is not picking it up as it should causing my development cycle to slow down drastically.
I have this dependency setup between my projects (5 projects, 3 wars, shared has my model etc. common-web has the common web components)
TD (war) -> common-web
CP (war) -> common-web
CP (war) -> Shared
Web (war) -> common-web
Web (war) -> Shared
Shared
common-web
With the following structure:
|- TD
|- Web
|- Shared
|- common-web
|- CP
| settings.gradle
| build.gradle
I have one gradle build file and one gradle settings file.
include 'TD', 'CP', 'Web', 'Shared', 'common-web'
allprojects {
repositories {
mavenCentral()
}
sourceCompatibility = 8
task wrapper(type: Wrapper) {
gradleVersion = '1.11'
}
subprojects {
apply plugin: 'java'
apply plugin: 'idea'
dependencies {
// dependencies...
}
}
project(':CP') {
apply plugin: 'war'
dependencies {
// dependencies...
compile project(':shared')
compile project(':common-web')
}
}
project(':web') {
apply plugin: 'war'
dependencies {
// dependencies...
compile project(':common-web')
compile project(':shared')
}
}
project(':shared') {
dependencies {
// dependencies...
}
}
project(':TD') {
apply plugin: 'war'
dependencies {
// dependencies...
compile project(':common-web')
}
}
project(':common-web') {
apply plugin: 'war' // needed to allow the providedComp
dependencies {
// dependencies...
}
}
Is there a way I should configure the gradle idea plugin to allow this to be build correctly? have found the make button does not work like I think it should work, ignoring the compile errors in the modules etc. I see that in the root of the project, IntelliJ creates a folder classes/artifacts and does not use the (correct) build in the module/lib folder.
The errors I'm seeing is the wrong include of the jars. For example the web project has a dependency to the TD project, which is defined nowhere.
Is this a bug in the gradle plugin of IntelliJ or am I doing something I shouldn't?