Spring boot multi project with gradle generates the same jar - spring

I'm new to gradle and I'm having trouble generating multiple jars with spring boot.
I generate two different builds, but when I run build A or build B, both are A
My project has the following structure:
root
├── facade
├─── rest-api
└─── web-api
├── dependencies
├─── services
├─── entities
└─── ...
├── settings.gradle
└── build.gradle
My intention is to have a mono repo of micro services. The micro services I will generate are the web api and rest api modules of the facade directory. These modules have dependencies of the module called dependencies as its name indicates.
As I described before, when I run the web api module, it's like I'm running rest api, even asking for its dependencies.
My settings.gradle:
rootProject.name = "root"
include ":root:facade:rest-api"
include ":root:facade:web-api"
include ":root:dependencies:entities"
include ":root:dependencies:services"
...
And my build.gradle
buildscript {
ext.kotlin_version = '1.3.61'
ext.spring_boot_version = '2.2.2.RELEASE'
ext.jjwt_version = '0.10.6'
ext.klockVersion = "1.7.3"
ext.queryDslVersion = '4.1.4'
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
}
}
group 'org.com'
version '0.1.0'
def javaProjects() {
return subprojects.findAll { new File(it.projectDir, "src").exists() }
}
subprojects {
repositories {
jcenter()
mavenCentral()
}
configure(javaProjects()) {
apply plugin: "java"
apply plugin: "java-library"
apply plugin: "org.jetbrains.kotlin.jvm"
apply plugin: 'kotlin'
apply plugin: "kotlin-spring"
apply plugin: "kotlin-jpa"
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "io.jsonwebtoken:jjwt-api:$jjwt_version"
implementation "io.jsonwebtoken:jjwt-impl:$jjwt_version"
implementation "io.jsonwebtoken:jjwt-jackson:$jjwt_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.soywiz.korlibs.klock:klock-jvm:1.7.3"
implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation "org.springframework.boot:spring-boot-starter-web"
implementation("org.springframework.boot:spring-boot-starter-security")
implementation "org.postgresql:postgresql:42.1.3"
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
implementation "khttp:khttp:1.0.0"
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude module: 'junit'
exclude module: 'mockito-core'
}
testImplementation('org.junit.jupiter:junit-jupiter:5.5.2')
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
testImplementation('com.ninja-squad:springmockk:2.0.0')
}
allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}
}
}
project(":root:dependencies:services") {
bootJar {
enabled = false
}
jar {
enabled = true
}
}
project(":root:dependencies:entities") {
bootJar {
enabled = false
}
jar {
enabled = true
}
}
...
I feel that I must have something wrong with the build.gradle, but I don't understand what.
I also have omitted the build.gradle files of the modules rest api and web api since I only have the dependencies and I have not considered it relevant.
I had previously worked with maven and followed this architecture. I don't know if gradle is the right thing to do, so I'm open to any advice you can give me.
Thank you for your attention.

As I mentioned in the comments, the correct way to include subprojects is to replace the path to the subproject with : i.e. if the subproject is in (from the root project) sub/project1, then the correct way to include it is:
include ':sub:project1'
Now as for your other question in the comments regarding:
Main class name has not been configured and it could not be resolved
You can simply do:
mainClassName = 'full.cannonical.name.of.MainClass'
If all the projects have a main class, then you need to do this in each project's build.gradle

Related

Create custom plugin that defines plugins and dependencies

My organization uses the same Gradle plugins and dependencies for a lot of our projects. My custom plugin knowledge is pretty weak, but what I'd like to do is wrap these plugins and dependencies into a single, standalone plugin. I'm stuck on understanding how to separate the plugins/dependencies required for the plugin versus the ones that I want to use in the consuming project. Here's a simple example that I put together based on the gradle custom plugin docs, and some information about storing the plugin in a maven repo to allow it to automatically download dependencies:
// build.gradle from standalone plugin
plugins {
id 'java-gradle-plugin'
id 'maven-publish'
// these ones I don't need in the plugin, just in the project where I apply the plugin
id 'war'
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'org.asciidoctor.convert' version '1.5.8'
}
group = 'org.sample'
version = '1.0.0'
publishing {
repositories {
maven {
url "../maven-repo"
}
}
}
gradlePlugin {
plugins {
greeting {
id = "org.sample.greeter"
implementationClass = "org.sample.GreetingPlugin"
}
}
}
dependencies {
implementation gradleApi() // I think I need this for the imports in GreetingPlugin.java
implementation localGroovy() // I think I would need this if GreetingPlugin was written in Groovy
// these ones I don't need in the plugin, just in the project where I apply the plugin
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'
}
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
}
// this is only needed in the project where I apply the plugin
// I believe this should be in the GreetingPlugin.java file though
test {
useJUnitPlatform()
}
and the backing class...
package org.sample;
import org.gradle.api.DefaultTask;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskAction;
class Greeting Plugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getTasks().create("hello", MyTask.class);
}
public static class MyTask extends DefaultTask {
#TaskAction
public void myTask() {
System.out.println("Hello, World!");
}
}
}
In the project I'm trying to consume the plugin, I have the following files:
// settings.gradle
pluginManagement {
repositories {
maven {
url "../maven-repo"
}
gradlePluginPortal()
}
}
// build.gradle
plugins {
id 'org.sample.greeter' version '1.0.0'
}
My thinking is that using the plugin in this way, the project inherits the plugins and dependencies listed in the plugin code. I think I'm close, as when I ./gradlew publish I can see the plugin being applied, but it doesn't like that the spring-starter-web dependency doesn't have a version (I know that when I do a multi-project gradle repo, I need to include the dependencyManagement block with mavenBOM, so maybe that's the key?) I'm trying to follow the SpringBoot gradle plugin for insight, but it's a bit too complicated for me.
So, is this the correct way to create a standalone plugin that includes plugins/dependencies baked in? And why isn't the spring dependency manager applying the versioning?
EDIT: I followed the link from #Alan Hay, and instead of a custom plugin, I tried to use the 'apply from'. However, it still doesn't work. Here's files based on that approach:
// build.gradle from 'parent' build.gradle
plugins {
id 'war'
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'org.asciidoctor.convert' version '1.5.8'
}
dependencies {
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'
}
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
}
test {
useJUnitPlatform()
}
and attempting to reference from another project, it's the only line in the file:
apply from: '<path-to-above>/build.gradle'
This error I get is the following:
script '<path-to-above>/build.gradle': 15: Only Project build scripts can contain plugins {} blocks
See https://docs.gradle.org/5.5.1/userguide/plugins.html#sec:plugins_block for information on the plugins {} block
# line 15, column 1.
plugins {
^
1 error
A standalone, binary plugin is the preferred approach when you need to share the same build logic across multiple independent projects. Additionally, good plugin design separates capabilities from convention. In this case, the capabilities are provided by Gradle and some third-party plugins, but you're adding your own conventions on top in this plugin.
When you're implementing this, you essentially need to push the code down one level. Anything that would be configuration in the build.gradle needs to be in your plugin's source code. Anything that would impact the classpath of the buildscript (i.e. buildscript { } or plugins { }) belongs in the dependencies of your plugin. The plugins { } block in your plugin should only have the build plugins required the build the plugin itself.
// build.gradle from standalone plugin
// plugins {} should contain only plugins you need in the build of the plugin itself
plugins {
id 'java-gradle-plugin'
id 'maven-publish'
}
group = 'org.sample'
version = '1.0.0'
dependencies {
implementation gradleApi()
// Dependencies for plugins you will apply to the target build
implementation 'io.spring.gradle:dependency-management-plugin:1.0.9.RELEASE'
implementation 'org.asciidoctor:asciidoctor-gradle-jvm:2.4.0'
implementation 'org.springframework.boot:spring-boot-gradle-plugin:2.2.4.RELEASE'
}
gradlePlugin {
plugins {
greeting {
id = "org.sample.greeter"
implementationClass = "org.sample.GreetingPlugin"
}
}
}
publishing {
repositories {
maven {
url "../maven-repo"
}
}
}
package org.sample;
import org.gradle.api.DefaultTask;
import org.gradle.api.Plugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.Project;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.testing.Test;
class Greeting Plugin implements Plugin<Project> {
#Override
public void apply(Project project) {
// Apply plugins to the project (already on the classpath)
project.getPluginManager().apply("war");
project.getPluginManager().apply("org.springframework.boot");
project.getPluginManager().apply("io.spring.dependency-management");
project.getPluginManager().apply(" org.asciidoctor.convert");
// Dependencies that you need for the code in the project that this plugin is applied
DependencyHandler dependencies = project.getDependencies();
dependencies.add(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, "org.springframework.boot:spring-boot-starter-web");
dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, "org.junit.jupiter:junit-jupiter-engine");
dependencies.add(JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME, springBootStarterTest(dependencies));
projects.getTasks().withType(Test.class, test -> {
test.useJUnitPlatform();
});
}
private Dependency springBootStarterTest(DependencyHandler dependencies) {
Map<String, String> exclude = new HashMap<>();
exclude.put("group", "org.junit.vintage");
exclude.put("module", "junit-vintage-engine");
return ((ModuleDependency) dependencies.module("org.springframework.boot:spring-boot-starter-test")).exclude(exclude);
}
}
This is more verbose due to being written in Java, but it is functionally equivalent to putting this in your project's build.gradle:
plugins {
id 'war'
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'org.asciidoctor.convert' version '1.5.8'
}
dependencies {
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'
}
testImplementation 'org.junit.jupiter:junit-jupiter-engine'
}
test {
useJUnitPlatform()
}

Gradle kotlin in Gradle5.2 Unresolved reference: dependtest

A multi module project with Kotlin source code, which used to work, stops working after upgrading to Gradle 5.2, because the Kotlin classes from the compile project('depend-test') dependency are not found.
Attempted to change plugin version
already viewed https://github.com/gradle/gradle/issues/8980
i defind Test class in project('depend-test')
object Test {
const val test = "123"
}
i want to use Test class in project('test-test')
package com.example.test.controller
import com.example.dependtest.Test
import org.slf4j.LoggerFactory
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
#RestController
#RequestMapping
class TestController {
private val log = LoggerFactory.getLogger(TestController::class.java)
#GetMapping(value = ["/test"])
fun test() {
log.info(Test.test)
}
}
when i want to build project('test-test') to jar where i used gradle bootJar。 I get this error:
> Task :test-test:compileKotlin FAILED
e: /Users/houshuai/Documents/dev/demo/test/test-test/src/main/kotlin/com/example/test/controller/TestController.kt: (3, 20): Unresolved reference: dependtest
e: /Users/houshuai/Documents/dev/demo/test/test-test/src/main/kotlin/com/example/test/controller/TestController.kt: (22, 18): Unresolved reference: Test
Expected Behavior
The Kotlin classes in the compile project('depend-test') dependency should be found.
Current Behavior
The Kotlin classes in the compile project('depend-test') dependency are not found:
Try adding this to your build.gradle file
bootJar {
enabled = false
}
jar {
enabled = true
}
Just in case someone else comes across this problem.
I created two modules, test-test and depend-test.
The depend-test project is test-test 's dependency.
I tried to call the parameters of depend-test, but it failed to compile and package.
Env
gradle-5.2.1
Kotlin 1.3.31
Springboot 2.1.4
java 1.8
step one
Edit settings.gradle
rootProject.name = 'demo'
include ":depend-test"
include ":test-test"
project(":depend-test").projectDir = file("depend/depend-test")
project(":test-test").projectDir = file("test/test-test")
I used the 1.3.31 version of the kotlin plug-in. The build.gradle file reads as follows
buildscript {
ext {
kotlinVersion = '1.3.31'
}
repositories {
mavenCentral()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/'}
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion"
}
}
plugins {
id 'org.springframework.boot' version '2.1.4.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.2.71'
id 'org.jetbrains.kotlin.plugin.spring' version '1.2.71'
}
allprojects {
apply plugin: 'idea'
apply plugin: 'kotlin'
repositories {
mavenCentral()
}
}
subprojects {
apply plugin: 'kotlin'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: "application"
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenLocal()
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/'}
maven { url "https://plugins.gradle.org/m2/" }
mavenCentral()
jcenter()
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
maven { url 'http://maven.springframework.org/release' }
maven { url 'http://maven.springframework.org/milestone' }
}
version = '1.0'
apply plugin: 'io.spring.dependency-management'
group = 'com.mutil.test'
sourceCompatibility = '1.8'
compileKotlin {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
jvmTarget = '1.8'
}
}
compileTestKotlin {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
jvmTarget = '1.8'
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'com.fasterxml.jackson.module:jackson-module-kotlin'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
}
dependencies {
subprojects.forEach {
archives(it)
}
}
repositories {
mavenCentral()
}
step two
build jar for test-test project ,I used two ways, but the results were the same.
terminal use cmd is ./gradlew :test-test:bootJar
user IDEA gradle tool
result
The class file written by kotlin in the submodule cannot be found.
I do not know if the lack of necessary plug-ins caused the failure to package properly.

dependencies added in subprojects.forEach in gradle multi-module kotlin DSL is not visible to sub projects

I have multi-module gradle project with kotlin dsl called stream-data-processing. It is in github here.
The build.gradle.kts file of root project is -
plugins {
base
java
}
allprojects {
group = "streams-data-processing"
version = "1.0"
repositories {
jcenter()
mavenCentral()
mavenLocal()
}
dependencies {
subprojects.forEach {
compile("org.apache.kafka:kafka-streams:2.2.0")
testImplementation("junit:junit:4.12")
}
}
}
settings.gradle.kts is -
rootProject.name = "stream-data-processing"
include ("word-count-demo")
I have some sub-project called word-count-demo.
The build.gradle.kts file for this sub project is -
plugins {
java
application
}
But the classes in kafka-streams are not available in word-count-demo.
when I did `gradle word-count-demo:dependencies, it doesn't show the kafka dependencies available to the sub project.
I don't want to explicitly specify the dependencies in every project.
What is the mistake that went wrong here?
It appears this would be adding the same dependencies multiple times. I think you need to flip it around and call dependencies inside subprojects, and outside of allprojects, like so:
allprojects {
group ...
version ...
repositories ...
}
subprojects {
dependencies {
compile("org.apache.kafka:kafka-streams:2.2.0")
testImplementation("junit:junit:4.12")
}
}

Sibling project seems not using it's dependency's build.gradle script

I have multi-module project with a structure like this:
|fullstack
\backend
|build.gradle
\frontend
|build.gradle
|build.gradle
|settings.gradle
Settings.gradle:
rootProject.name = 'fullstack'
include 'backend'
include 'frontend'
backend depends on ktor, so it's build.gradle contains repository:
maven { url "https://dl.bintray.com/kotlin/ktor" }
And backend module builds well.
Then I want to share some common classes between backend and frontend. I could make third module api and make others depend on it. But now I want to avoid third module and just make frontend to depend on backend.
I added the dependency to the frontend's gradle.build:
compile project (':backend')
and tried to build the whole project, but got an error:
Could not resolve all files for configuration
':frontend:compileClasspath'.
Could not find io.ktor:ktor-server-netty:0.9.1. Searched in the following locations:
https://repo.maven.apache.org/maven2/io/ktor/ktor-server-netty/0.9.1/ktor-server-netty-0.9.1.pom
https://repo.maven.apache.org/maven2/io/ktor/ktor-server-netty/0.9.1/ktor-server-netty-0.9.1.jar
Required by:
project :frontend > project :backend
It obviously didn't scan repositories of backend module. Why?
UPDATE
I've added repositories as said in answers. But when I try to import in frontend/main.kt any class from backend I get:
:frontend:compileKotlin2Jse: D:\...\frontend\src\main\kotlin\main.kt: (1, 8): Unresolved reference: com
e: D:\...\frontend\src\main\kotlin\main.kt: (2, 8): Unresolved reference: com
How to make it visible for frontend?
Root build.gradle:
group 'com.norg.parts'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_ver = '1.2.50'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_ver"
}
}
allprojects {
project.ext {
kotlin_version = kotlin_ver
ktor_version = '0.9.1'
}
repositories {
jcenter()
mavenCentral()
maven { url 'https://dl.bintray.com/kotlin/ktor' }
maven { url 'https://dl.bintray.com/kotlin/kotlinx' }
maven { url 'https://mvnrepository.com/artifac/' }
maven { url "https://dl.bintray.com/kotlin/exposed" }
}
}
apply plugin: 'java'
apply plugin: 'kotlin'
sourceCompatibility = 1.8
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
jar {
manifest {
attributes 'Main-Class': 'com.norg.parts.MainKt'
}
//TODO include jar from frontend!
}
The problem is that there can be 2 dependencies / repositories blocks. The first one is in the buildscript which looks like this:
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
This is responsible for setting up tooling and plugins. Then when you set up the config of your project itself you can do several things. If you don't have a multi-module project you just dump it into your build.gradle then the file looks like this:
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
repositories {
jcenter()
mavenCentral()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
dependencies {
compile "io.ktor:ktor-server-core:$ktor_version"
}
In your case you should use either allprojects which means that all configuration which you put in that block will be applied to all your projects (including root), or subprojects. In the latter case, the block will be applied to only your subprojects (root not included). This looks like this:
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
subprojects {
repositories {
jcenter()
mavenCentral()
maven { url "https://dl.bintray.com/kotlin/ktor" }
}
dependencies {
compile "io.ktor:ktor-server-core:$ktor_version"
}
}
Your problem is that you did not add the repository itself to your project config, only your buildscript config.
If you are planning to mix frontend and backend code you can now use Kotlin Multiplatform Projects. I've written about this here.
Response to your edit:
You really need to move all this to a subprojects block:
subprojects {
apply plugin: 'java'
apply plugin: 'kotlin'
sourceCompatibility = 1.8
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
jar {
manifest {
attributes 'Main-Class': 'com.norg.parts.MainKt'
}
//TODO include jar from frontend!
}
}
and you also need to add the backend project as a dependency to your frontend project in that project's build.gradle. I'd strongly advise you to read the Gradle Documentation since this is a Gradle issue not a Kotlin issue whatsoever.
Dependencies are transitive, but repositories are not. This is why most projects have a allprojects block in the root build file defining the repositories for all subprojects at the same time.

Kotlin Gradle Could not find or load main class

I tried to copy the Spring Boot Kotlin sample project https://github.com/JetBrains/kotlin-examples/tree/master/tutorials/spring-boot-restful. I Added some more dependencies and when I tried to build the executable jar and run it, I got the error:
Could not find or load main class...
Gradle build script:
buildscript {
ext.kotlin_version = '1.1.3' // Required for Kotlin integration
ext.spring_boot_version = '1.5.4.RELEASE'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Required for Kotlin integration
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
}
}
/*plugins {
id 'org.springframework.boot' version '2.0.0.RELEASE'
}*/
apply plugin: 'kotlin' // Required for Kotlin integration
apply plugin: "kotlin-spring" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin
apply plugin: 'org.springframework.boot'
jar {
baseName = 'gs-rest-service'
version = '0.1.0'
from {
(configurations.runtime).collect {
it.isDirectory() ? it : zipTree(it)
}
}
manifest {
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.Applicationkt'
}
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin/'
test.java.srcDirs += 'src/test/kotlin/'
}
repositories {
jcenter()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // Required for Kotlin integration
compile("org.springframework.boot:spring-boot-starter-web")
compile group: 'org.apache.camel', name: 'camel-quartz2', version: '2.20.2'
compile group: 'org.apache.camel', name: 'camel-http4', version: '2.20.2'
compile group: 'org.apache.camel', name: 'camel-docker', version: '2.20.2'
compile group: 'org.apache.camel', name: 'camel-aws', version: '2.20.2'
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Change Applicationkt to ApplicationKt will work, and BTW you may upgrade Kotlin version to 1.3.50.
By Applicationkt I mean the one in this line:
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.Applicationkt'
Kotlin compiles the Application file in two different files:
one file called Application.class with the Springboot things
another file called ApplicationKt.class with the main method
In this second file is where the main function is located at, so you have to use this name in the build.gradle file.
mainClassName = 'org.jetbrains.kotlin.demo.ApplicationKt'
Update your build.gradle to
jar {
manifest {
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.ApplicationKt'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
with an upper case K in ApplicationKt.
This is required because of the way Kotlin compiles to Java Bytecode. The fun main() function in Kotlin is not attached to any class, but Java always requires a class and does not support classless functions.
The Kotlin compiler has to create a Java class. Because you already defined a class Application it created one with the suffix Kt for the functions in your Kotlin file org/jetbrains/kotlin/demo/Application.kt. You have to set this class so that the JVM can find it.
BTW a Jar file is just a Zip file, you can unpack it and see for yourself if the ApplicationKt.class is there.
For me the main function needed to be outside the class body
#SpringBootApplication
#Configuration
class Application
(private val locationRepository: LocationRepository,
) : CommandLineRunner {
override fun run(vararg args: String?) {
whatever()
}
}
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
Indeed, Kotlin create file ApplicationKt.class in the jar if your main class file is named Application.kt. You have to add the following lines:
apply plugin: 'kotlin'
apply plugin: 'application'
mainClassName = 'org.jetbrains.kotlin.demo.ApplicationKt'
If you use the classic jar plugin, you can do as below (which is described in previous responses):
jar {
manifest {
attributes 'Main-Class': 'org.jetbrains.kotlin.demo.ApplicationKt'
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
However, my preference is to use bootJar plugin which is much clear and which allow me to use layered jars for example:
bootJar {
layered() // Not useful if you don't want to use layered jars
}

Resources