-
Notifications
You must be signed in to change notification settings - Fork 41.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spring Boot 3 Native Fails to Start with Kotlin @JvmStatic Main Method #32918
Comments
Looks like we deduce the wrong class name for the application context initializer: Workaround until fixed: @SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
} which is the default code the initializer generates. |
You're right. The workaround doesn't work for me since I have to set the main class in |
There is. The default class name for free functions is the filename + "Kt", so in your case
You can rename the class by using @file:JvmName("SomeOtherClassName")
See here: https://kotlinlang.org/docs/java-to-kotlin-interop.html#package-level-functions |
Got it. Thanks for the link. 🙏 |
This shouldn't be necessary. |
I wonder if we should set the main class in the Kotlin extension. Things are a little odd at the moment, even without AOT, as the companion object's class is used for logging:
If we set the main class, it looks like this instead:
That's after making these changes to the extensions: diff --git a/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt b/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt
index 8a1269d06b..3ae3cdbc6c 100644
--- a/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt
+++ b/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2017 the original author or authors.
+ * Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,12 @@ import org.springframework.context.ConfigurableApplicationContext
* @author Sebastien Deleuze
* @since 2.0.0
*/
-inline fun <reified T : Any> runApplication(vararg args: String): ConfigurableApplicationContext =
- SpringApplication.run(T::class.java, *args)
+inline fun <reified T : Any> runApplication(vararg args: String): ConfigurableApplicationContext {
+ val application = SpringApplication(T::class.java)
+ application.setMainApplicationClass(T::class.java)
+ return application.run(*args)
+}
+
/**
* Top level function acting as a Kotlin shortcut allowing to write
@@ -38,5 +42,8 @@ inline fun <reified T : Any> runApplication(vararg args: String): ConfigurableAp
* @author Sebastien Deleuze
* @since 2.0.0
*/
-inline fun <reified T : Any> runApplication(vararg args: String, init: SpringApplication.() -> Unit): ConfigurableApplicationContext =
- SpringApplication(T::class.java).apply(init).run(*args)
+inline fun <reified T : Any> runApplication(vararg args: String, init: SpringApplication.() -> Unit): ConfigurableApplicationContext {
+ val application = SpringApplication(T::class.java).apply(init)
+ application.setMainApplicationClass(T::class.java)
+ return application.run(*args)
+} WDYT, @sdeleuze, does this make sense? |
Looking more closely, I think this is a bug in the code where we deduce the main application class. It can effect Java too if you write some (convoluted) code that has a similar structure to the code the Kotlin's compiler generates: package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
Companion.main(args);
}
static class Companion {
static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
}
At the moment we identify the main class by walking the stack and finding the first frame for a method named We can't narrow things down by looking for a particular type of method as it doesn't work with Graal. For the reasons above, I'm also not sure that we can safely look for the last method named |
Here's a change that corrects the main class only when a companion object is used: diff --git a/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt b/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt
index 8a1269d06b..2405a00f2e 100644
--- a/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt
+++ b/spring-boot-project/spring-boot/src/main/kotlin/org/springframework/boot/SpringApplicationExtensions.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2017 the original author or authors.
+ * Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,15 @@ import org.springframework.context.ConfigurableApplicationContext
* @author Sebastien Deleuze
* @since 2.0.0
*/
-inline fun <reified T : Any> runApplication(vararg args: String): ConfigurableApplicationContext =
- SpringApplication.run(T::class.java, *args)
+inline fun <reified T : Any> runApplication(vararg args: String): ConfigurableApplicationContext {
+ val application = SpringApplication(T::class.java)
+ val possibleCompanionClass = object{}.javaClass.enclosingClass
+ if (possibleCompanionClass.enclosingClass != null && possibleCompanionClass.kotlin.isCompanion) {
+ application.setMainApplicationClass(possibleCompanionClass.enclosingClass)
+ }
+ return application.run(*args)
+}
+
/**
* Top level function acting as a Kotlin shortcut allowing to write
@@ -38,5 +45,11 @@ inline fun <reified T : Any> runApplication(vararg args: String): ConfigurableAp
* @author Sebastien Deleuze
* @since 2.0.0
*/
-inline fun <reified T : Any> runApplication(vararg args: String, init: SpringApplication.() -> Unit): ConfigurableApplicationContext =
- SpringApplication(T::class.java).apply(init).run(*args)
+inline fun <reified T : Any> runApplication(vararg args: String, init: SpringApplication.() -> Unit): ConfigurableApplicationContext {
+ val application = SpringApplication(T::class.java).apply(init)
+ val possibleCompanionClass = object{}.javaClass.enclosingClass
+ if (possibleCompanionClass.enclosingClass != null && possibleCompanionClass.kotlin.isCompanion) {
+ application.setMainApplicationClass(possibleCompanionClass.enclosingClass)
+ }
+ return application.run(*args)
+} It's pretty gross. It's using an anonymous inner class ( |
I had the same problem in Java and found a workaround. Maybe that'll help you somehow.
|
Have the same problem. No luck with workarounds that were mentioned here.
plugins {
id("org.springframework.boot") version "3.0.5"
id("io.spring.dependency-management") version "1.0.13.RELEASE"
kotlin("jvm") version "1.8.10"
kotlin("plugin.spring") version "1.8.10"
kotlin("kapt") version "1.8.10"
}
...
tasks.bootBuildImage {
builder.set("paketobuildpacks/builder:tiny")
environment.set(
mapOf(
"BP_NATIVE_IMAGE" to "true",
"BP_NATIVE_IMAGE_BUILD_ARGUMENTS" to
"""
--verbose
--no-fallback
--initialize-at-build-time=org.slf4j.LoggerFactory,ch.qos.logback
--trace-class-initialization=ch.qos.logback.classic.Logger
--initialize-at-run-time=io.netty
""".trimIndent()
)
)
}
|
We can confirm @takanoro's problem, even tried the approach to rename the kotlin main class accordingly DemoMain.kt
DemoApp.kt
build.gradle:
I tried both with 3.0 and 3.1 spring versions.
|
so is there any temporary solution to this problem? |
@SpringBootApplication
class Sb32918Application
fun main(args: Array<String>) {
runApplication<Sb32918Application>(*args)
} works for me, try for yourself: Could you please attach a sample for a non-working app? |
@mhalbritter my mistake, it missed mainClass because of I configured the native-maven-plugin incorrectly, thanks for your help. |
This happened when upgrading to spring-boot |
I think I'm facing the same issue with Spring 3.1.2. The application compiles but when I run it I get an error.
My build.gradle.kts: import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
// https://plugins.gradle.org/plugin/org.springframework.boot
id("org.springframework.boot") version "3.1.2"
// https://plugins.gradle.org/plugin/io.spring.dependency-management
id("io.spring.dependency-management") version "1.1.3"
// https://github.com/graalvm/native-build-tools
id("org.graalvm.buildtools.native") version "0.9.24"
// https://plugins.gradle.org/plugin/org.jetbrains.kotlin.jvm
kotlin("jvm") version "1.9.0"
// https://plugins.gradle.org/plugin/org.jetbrains.kotlin.plugin.spring
kotlin("plugin.spring") version "1.9.0"
}
group = "dev.mbo"
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive")
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
// https://mvnrepository.com/artifact/io.netty/netty-resolver-dns-native-macos
implementation("io.netty:netty-resolver-dns-native-macos:4.1.96.Final:osx-aarch_64")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
developmentOnly("org.springframework.boot:spring-boot-devtools")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-testcontainers")
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation("org.testcontainers:junit-jupiter")
}
tasks {
withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs += "-Xjsr305=strict"
jvmTarget = "17"
}
}
withType<Test> {
useJUnitPlatform()
}
withType<Copy> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
bootJar {
archiveFileName = "${project.name}-all.jar"
exclude("application-sec*.yml")
}
withType<Wrapper> {
// https://gradle.org/releases/
gradleVersion = "8.2.1"
distributionType = Wrapper.DistributionType.ALL
}
graalvmNative {
binaries {
named("main") {
mainClass.set("dev.mbo.linkshortener2.ApplicationKt")
}
}
}
} Without setting the main class like a few lines above the nativeCompile task fails. My main looks like this: package dev.mbo.linkshortener2
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
} Starting the application from the bootJar works fine. Setting mainClass.set("dev.mbo.linkshortener2.Application") complained about not finding the main method. |
Can you share the code with us so I can take a look? |
Having the same issue on Spring Boot 3.1.0, Kotlin , Gradle.
my root class:
This is my gradle.build part related to the graalvm and native image
|
@mhalbritter uploaded to https://github.com/mbogner/link-shortener2 highly work in progress but it runs as jar on my mac M1. didn't work on the dockerisation scripts yet that are included |
I tried various workarounds, none worked. @file:JvmName("MyApp")
package com.example.my
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
@SpringBootApplication
@EnableWebFluxSecurity
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
} application {
mainClass.set("com.example.my.MyApp")
} When running
I tried with 3.1.2 and 3.1.0 EDIT: I gave another shot - I rewrote main in Java and it seems that the same error ( |
I made a project that reproduces this issue - it has two branches: one with Kotlin main and another with Java main. Both fail with the same error. https://github.com/kkocel/classnotfoundrepero |
@kkocel I played around with your
|
Thanks for analyzing, @rt-works! |
@mhalbritter So is there a chance that this issue will get resolved soon? |
In my case my problem was just solved by upgrading to |
@rt-works do you have a minimal working example? do yo use kotlin 1.9 + native buildtools 0.9.24? |
it works for me with kotlin 1.8.22 + native buildtools 0.9.24. I'll prepare the example |
sorry for the confusion. I thought you managed to run this against Kotlin 1.9.0. So for now we pin-pointed that Kotlin 1.9.0 is causing this issue. |
Any update for Kotlin Having a simple project, I have the same issue, I'm on:
# .kts
springBoot {
mainClass.set("com.iptiq.apptemplate.AppTemplateApplicationKt")
}
graalvmNative {
binaries {
named("main") {
mainClass = "foo.baz.apptemplate.AppTemplateApplicationKt"
buildArgs(
"-H:+ReportExceptionStackTraces",
"-H:EnableURLProtocols=http,https",
"--initialize-at-run-time=io.netty.handler.ssl.BouncyCastleAlpnSslUtils",
"--initialize-at-build-time=org.slf4j.impl.StaticLoggerBinder,org.slf4j.LoggerFactory,ch.qos.logback.core.spi.AppenderAttachableImpl,ch.qos.logback.core.status.StatusBase,ch.qos.logback.classic.Level,ch.qos.logback.core.status.InfoStatus,ch.qos.logback.classic.PatternLayout,ch.qos.logback.core.CoreConstants,ch.qos.logback.classic.Logger,ch.qos.logback.core.util.Loader,ch.qos.logback.core.util.StatusPrinter"
)
}
}
} I do just same stuff but |
I can confirm that this issue no longer occurs as of SB 3.2 + Kotlin 1.9.20. Main: package com.example.graalvmrepro
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class GraalvmreproApplication
fun main(args: Array<String>) {
runApplication<GraalvmreproApplication>(*args)
} gradle script: application {
mainClass.set("com.example.graalvmrepro.GraalvmreproApplicationKt")
} |
@kkocel thanks, but, as far as I can tell, the issue as originally reported still occurs with Spring Boot 3.2 and Kotlin 1.9.20. In fact, it's got slightly worse as the error message is no longer as helpful as it was:
I think we should consider refining or reverting #38188, particularly while this issue is unresolved. |
Bug Report for Spring Boot 3 Native (GraalVM 22.3)
Having a simple Spring Boot application (generated on start.spring.io), changing the main class to:
And building the native image (via
./gradlew nativeCompile
) gives you an executable that fails to start with:Let me know if you need more information. Thanks.
The text was updated successfully, but these errors were encountered: