Gradle: multi-project build with spring-boot - java

I'm new to Gradle and I'm just getting started with it using a multi-project build. Currently, I'm using spring-boot for services and i have the following directory structure
|- api-web (module)
|-- build.gradle
|- api-admin (module)
|-- build.gradle
|- build.gradle (root)
|- settings.gradle
Below are the configurations of the root
build.gradle (root)
plugins {
id 'java'
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
}
description = "Company Multi-Project"
ext {
springBootVersion = '2.2.2.RELEASE'
}
bootJar {
enabled = false
}
allprojects {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenCentral()
}
}
subprojects {
group = 'com.company'
version = '0.0.1-SNAPSHOT'
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
jar {
enabled = true
}
bootJar {
enabled = false
}
dependencies {
// Lombok annotation processor
annotationProcessor 'org.projectlombok:lombok'
compileOnly 'org.projectlombok:lombok'
}
}
settings.gradle (root)
rootProject.name = 'core'
include 'common'
include 'api-web'
include 'api-admin'
Gradle configurations of the modules
api-admin (module)
jar {
manifest {
attributes 'Main-Class': 'com.company.apiadmin.ApiAdminApplication'
}
}
dependencies {
implementation project(':common')
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
I have added jar closure to prevent error on runtime. This avoids the issue where it fails to run saying "No Manifest"
However even after adding that jar closure, now there is a new issue
Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
Now I know this says class not found, but my question is how does it occur and why and how to overcome this. I have tried most of the suggestions in Medium, StackOverflow and in Blog posts. Nothing helped. Your help will be highly appreciated. Thanks

I found the solution for my self, it was a bit tricky to find but finally, I found it
The main point is that you should definitely add the spring-boot-gradle-plugin and that will resolve all of your NoClassDef errors and No Manifest Found errors. To add this you should define a build script in your root build.gradle file
buildscript {
ext {
springBootVersion = "2.2.2.RELEASE"
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
}
}
Then you should define the plugins for your project
plugins {
id 'java'
id 'org.springframework.boot' version '2.2.2.RELEASE'
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
}
then you can define all the subprojects and allprojects as you normally would.
Then in the modules, in every sub-module just add these lines
apply plugin: 'org.springframework.boot'
bootJar {
launchScript() // optional
}
dependencies {
// your module-specific dependencies
}
and the dependencies as you normally would.
That's it, now you can build using gradle and execute the .jar files as you normally would. :-)

Related

Creating a Modularized SpringBoot app with Gradle

I am trying to setup a simple application with one Java module. There is the parent application and a module called "feature".
Despite the IDE autocompletion working ok I keep getting this error during build:
error: module not found: spring.web
My current setup is:
Java 12
SpringBoot 2.5.0-SNAPSHOT
Gradle 6.8.3
I have done a dozen other apps using "gradle modules" and I am using the same directory structure:
The parent build.gradle is this:
plugins {
id 'org.springframework.boot' version '2.5.0-SNAPSHOT'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.mrv'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_11
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
java {
modularity.inferModulePath = true
}
allprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
}
dependencies {
implementation project(':feature')
}
The "feature" module-info.java (where the problem is occurring) is this:
module com.mrv.modules.feature {
requires spring.web;
}
I based myself on some Maven projects and also the Gradle documentation. I also tried to put my main class ModulesApplication inside its own module with the same results.
Am I missing something?
A couple of hours later I found the solution and it was quite simple: apply modularity.inferModulePath = true to all projects:
plugins {
id 'org.springframework.boot' version '2.5.0-SNAPSHOT'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.mrv'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_11
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
allprojects {
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}
/* >>>> MOVED HERE <<<<*/
java {
modularity.inferModulePath = true
}
}
dependencies {
implementation project(':feature')
}
This is a sample project I made to use as a reference in the future.

Is it possible to get classes from root module in gradle?

I have multi module spring boot project. Root project is "BookStore", and child module is "api". I try to use in "api" classes from "bookstore". But I have error:
A problem occurred evaluating project ':api'.
Project with path ':BookStore' could not be found in project ':api'.
My root settings.gradle:
pluginManagement {
plugins {
id 'org.springframework.boot' version "2.3.3.RELEASE"
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
}
}
rootProject.name = 'BookStore'
include 'api'
build.gradle of the root:
plugins {
id 'org.springframework.boot'
id 'io.spring.dependency-management'
id 'java'
id 'idea'
}
allprojects {
group = 'com.aleksandr'
version = '1.0-SNAPSHOT'
description = 'BookStore'
}
subprojects {
repositories {
mavenCentral()
}
apply {
plugin("io.spring.dependency-management")
}
}
java.sourceCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenLocal()
maven {
url = uri('https://repo.maven.apache.org/maven2/')
}
}
dependencies {
implementation project(':api')
}
And build.gradle of the child module:
plugins {
id 'org.springframework.boot'
id 'io.spring.dependency-management'
id 'java'
id 'idea'
}
repositories {
mavenCentral()
maven {
url = uri('https://repo.maven.apache.org/maven2/')
}
}
dependencies {
compile project(":BookStore")
implementation 'org.springframework.boot:spring-boot-starter-web'
}
What am I doing wrong? How can I get classes from root module?
If you need to create a dependency from a subproject (here, api) to the rootProject, you must use one of the following dependency notations:
child module build.gradle
dependencies {
api(project(":")) // ":" is the identifier for the rootProject
// OR
api(rootProject) // "rootProject" varible points to the root Project instance.
}
But there is another issue in your example: you are creating a dependency cycle between the rootProject and api subproject, which is not allowed.
IMO, your api subproject should not depend on root project.

IntelliJ: Unable to use class from jar dependency

I am trying to test setting up a minimal project, building and exporting it as a jar, and using that project as a dependency in another. I am using Spring for this. I have two projects, makeajar and useajar. In makeajar, I set up a simple project with a Java class called Dinner. I want to import the jar for this project as part of the library in useajar, and from useajar instantiate a Dinner object.
However, the issue I'm getting is that I can't import the Dinner class. When I do, the import shows red:
Is there something I'm doing wrong in this process?
Things I've already tried:
Invalidating caches and restarting
Creating a fresh project
Adding makeajar as a module dependency (note: This was already there, and also appeared in the Project Structure -> Libraries)
How I produce the jar file from makeajar:
Go to project root directory and run ./gradlew build
Things I'm noticing:
Although makeajar appears in my module dependencies and libraries, it does not appear in the "External Libraries" folder in the useajar project.
I can gradle refresh fine, and if I comment out the attempts to import the Dinner object, I build.
I can easily get various popular dependencies (i.e. Jackson, guava) from MavenCentral and use these right out of the box. The issue is only occurring with my makeajar dependency.
makeajar build.gradle:
plugins {
id 'org.springframework.boot' version '2.2.12.BUILD-SNAPSHOT'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.makingajar'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
useajar build.gradle:
plugins {
id 'org.springframework.boot' version '2.2.12.BUILD-SNAPSHOT'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
id 'idea'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
apply plugin: 'idea'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
mavenLocal {
flatDir {
dirs projectDir.toString() + '/libs'
}
}
}
allprojects {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
implementation 'com.makingajar:makeajar2:0.0.1-SNAPSHOT'
implementation 'com.fasterxml.jackson.core:jackson-core:2.11.3'
}
}
test {
useJUnitPlatform()
}
IntelliJ version 2020.1.4
Gradle version 6.6.1

Unable to use outside dependencies within custom gradle plugin implementation

When attempting to use outside dependencies within a custom gradle plugin I'm building, I am not able to import or use them in any regard.
I've attempted to specify in both the build script and the normal dependencies closure my dependencies. I'm using Gradle 5.5 (wrapper script) and I am using the buildSrc method of writing a custom gradle plugin.
// Necessary if loading custom plugins
apply plugin: 'java-gradle-plugin'
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.google.code.gson:gson:2.8.5'
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
group 'com.foo'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
gradlePlugin {
plugins {
greeterPlugin {
id = 'com.foo.dbcreation-plugin'
implementationClass = 'com.foo.dbcreation.DbCreation'
}
}
}
dependencies {
compile 'com.google.code.gson:gson:2.8.5'
}
There are quite a few issues I see here.
buildscript does not control the dependencies for your plugin implementation.
Use the plugins {} DSL block to apply plugins. It is the preferred way: https://docs.gradle.org/current/userguide/plugins.html#sec:plugins_block
Should be using implementation over compile since compile is deprecated as noted in https://docs.gradle.org/current/userguide/java_plugin.html#tab:configurations
With that said, your Gradle file should be like:
plugins {
id 'java-gradle-plugin'
id 'eclipse'
id 'idea'
}
group 'com.foo'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
jcenter()
}
gradlePlugin {
plugins {
greeterPlugin {
id = 'com.foo.dbcreation-plugin'
implementationClass = 'com.foo.dbcreation.DbCreation'
}
}
}
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
}
I figured out what my issue was. For projects being built using the buildSrc directory, you need to have the build.gradle file reside in that directory instead of the root project directory (where the build.gradle normally lives). I just converted the project to a normal gradle project and it works just fine.

spring boot gradle plugin in a multi module project - error finding mainClass

I am using Spring boot gradle plugin version 1.5.1 RELEASE as shown below. The build fails at webProject complaining about missing property 'mainClass' and works only when I run 'webProject:build'. Is this the expected usage?
Edit: Updated the build script and removed 'spring-boot' plugin from allProjects. Had to add 'bootRepackage' in web project as it was failing at this step - with the same error. Adding the 'bootRepackage' didn't help.
buildscript {
ext {
springBootVersion = '1.5.1.RELEASE'
}
repositories {
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.1.RELEASE")
}
}
plugins {
id 'org.springframework.boot' version '1.5.1.RELEASE'
}
defaultTasks 'clean', 'build'
apply plugin: 'java'
apply plugin: 'war'
sourceCompatibility = 1.7
targetCompatibility = 1.7
allprojects {
apply plugin: 'java'
//apply plugin: 'org.springframework.boot' -- Commented out based on the answer
repositories {
mavenLocal()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
//all dependencies
}
}
project('aProject') {
dependencies {
compile(project(':bProject'))
}
}
project('webProject') {
apply plugin: 'war'
apply plugin: 'org.springframework.boot'
war {
baseName = 'webProject'
version = '1.0.0-SNAPSHOT'
}
dependencies {
compile(project(':aproject'))
compile(project(':bProject'))
compile 'org.springframework.boot:spring-boot-starter-tomcat'
}
springBoot {
mainClass = 'com.abc.SomeApplication'
}
bootRepackage{
enabled = false
mainClass = 'com.abc.SomeApplication'
}
}
Do not use Spring Boot gradle plugin in main project, only in webProject sub-module.
I have a similar problem in a multi module Spring boot project.
When compiling module A which doesn't have a main class. The main class is in a different module (module B).
I add a plugin in that module A's build.gradle.
apply plugin: 'application'
mainClassName = "module B.ITS_MAIN_CLASS"
Then it works.

Categories

Resources