Spring Boot + Kotlin + Gradle - Error: Main method not found in class - spring-boot

I'm learning spring boot with Kotlin (since I come from Android with Kotlin). I set it up with gradle. In my local machine everything works just fine. But I'm having a few issues while trying to deploy it to Heroku.
This is the error I'm getting:
Error: Main method not found in class com.markoid.packit.PackitApplication, please define the main method as:
2021-07-01T20:58:51.075484+00:00 app[web.1]: public static void main(String[] args)
2021-07-01T20:58:51.075581+00:00 app[web.1]: or a JavaFX application class must extend javafx.application.Application
I read on other posts that I need to add system.properties file in the root, so I did, but nothing changes.
system.properties
java.runtime.version=11
And this is my build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.5.1"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.5.10"
kotlin("plugin.spring") version "1.5.10"
}
group = "com.markoid"
version = "1.0.0-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
repositories {
mavenCentral()
}
dependencies {
// Spring Boot Core
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
// Joda Time library
implementation("joda-time:joda-time:2.10")
// Json Web Token
implementation("io.jsonwebtoken:jjwt-impl:0.11.1")
implementation("io.jsonwebtoken:jjwt-api:0.11.1")
implementation("io.jsonwebtoken:jjwt-jackson:0.11.1")
// Serializers
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
// Documentation
implementation("io.springfox:springfox-swagger2:2.6.1")
// Kotlin related
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Testing Frameworks
testImplementation("org.springframework.boot:spring-boot-starter-test")
}
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = "11"
}
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = "com.markoid.packit.PackitApplication"
}
}
My app file is as simple as this:
#SpringBootApplication
class PackitApplication
fun main(args: Array<String>) {
runApplication<PackitApplication>(*args)
}
Does someone know what I'm missing? This is the first project on spring I'm trying to deploy on heroku, so please bare with me.
Thank you in advance.

I've faced with the same issue. My problematic configuration is:
Kotlin 1.5.21
Spring boot 2.5.2
Gradle 7.1.1
The problem was that in IDEA it can be run, but cannot not using gradlew command line. I went and checked all old projects. They were built and run without any problem. The difference was in versions. All my previous project had lower versions in all positions I mentioned above. So I suggested that the problem was in the version, but what tool it was? Kotlin, Spring, Gradle? I have not yet found the guilty (and no time to go into deep of this problem now), but I found a solution.
If you open your jar file in any archiver (eg. WinRAR) and look at the MANIFEST.MD you will see a line starting with 'Start-Class', there is your main class and it must end with 'Kt' suffix, for example
Start-Class: me.sigest.fiveplus.FiveplusApplicationKt
In my failed jar it was not. To fix it I changed the code in build.gradle.kts file and set
springBoot {
mainClass.set("com.example.MyMainClassKt")
}
Despite the fact that in reality my class is called MyMainClass (without suffix Kt) and it helped
P.S. all my old boot jars contain the correct name of main class with Kt. I suppose the problem is in Gradle

I just had to a few things to make it work:
In the build.gradle.kts, I removed the tasks with type jar, and added this:
springBoot {
mainClass.set("com.markoid.packit.PackitApplicationKt")
}
I needed to add a Procfile, with the following:
web: java -Dserver.port=$PORT $JAVA_OPTS -jar build/libs/packit-1.0.0-SNAPSHOT.jar
Such file will tell heroku the specific command I want to be executed to run the generated jar.

Have you tried changing the main method like it is being suggested in the error message, i.e. public static void main(String[] args)

Related

How to create a Jar file of a spring boot application using Intellij and gradle [duplicate]

I'm trying to build an executable jar in Spring Boot + Gradle project, but for now nothing works. Here is the simplest possible structure. Possibly, something is missing in Gradle configuration.
Gradle:
buildscript {
ext {
springBootVersion = '1.5.8.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
jar {
manifest {
attributes 'Main-Class': 'com.example.demo.DemoApplication'
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
}
Main config file:
#RestController
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#GetMapping(value = "/")
public String index() {
return "index";
}
}
When I ran the jar file like java -jar 1.jar, I got this exception:
[main] ERROR org.springframework.boot.SpringApplication - Applicati
on startup failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to proces
s import candidates for configuration class [com.example.demo.DemoApplication];
nested exception is java.lang.IllegalArgumentException: No auto configuration cl
asses found in META-INF/spring.factories. If you are using a custom packaging, m
ake sure that file is correct.
at org.springframework.context.annotation.ConfigurationClassParser.proce
ssDeferredImportSelectors(ConfigurationClassParser.java:556)
at org.springframework.context.annotation.ConfigurationClassParser.parse
(ConfigurationClassParser.java:185)
at org.springframework.context.annotation.ConfigurationClassPostProcesso
r.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:308)
at org.springframework.context.annotation.ConfigurationClassPostProcesso
r.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228)
at org.springframework.context.support.PostProcessorRegistrationDelegate
.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.ja
va:272)
at org.springframework.context.support.PostProcessorRegistrationDelegate
.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:92)
at org.springframework.context.support.AbstractApplicationContext.invoke
BeanFactoryPostProcessors(AbstractApplicationContext.java:687)
at org.springframework.context.support.AbstractApplicationContext.refres
h(AbstractApplicationContext.java:525)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationConte
xt.refresh(EmbeddedWebApplicationContext.java:122)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.
java:693)
at org.springframework.boot.SpringApplication.refreshContext(SpringAppli
cation.java:360)
at org.springframework.boot.SpringApplication.run(SpringApplication.java
:303)
at org.springframework.boot.SpringApplication.run(SpringApplication.java
:1118)
at org.springframework.boot.SpringApplication.run(SpringApplication.java
:1107)
at com.example.demo.DemoApplication.main(DemoApplication.java:13)
Caused by: java.lang.IllegalArgumentException: No auto configuration classes fou
nd in META-INF/spring.factories. If you are using a custom packaging, make sure
that file is correct.
at org.springframework.util.Assert.notEmpty(Assert.java:277)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelecto
r.getCandidateConfigurations(AutoConfigurationImportSelector.java:153)
at org.springframework.boot.autoconfigure.AutoConfigurationImportSelecto
r.selectImports(AutoConfigurationImportSelector.java:95)
at org.springframework.context.annotation.ConfigurationClassParser.proce
ssDeferredImportSelectors(ConfigurationClassParser.java:547)
... 14 common frames omitted
What might be wrong?
In Boot 2.x, the bootJar and bootWar tasks are responsible for packaging the application.
The bootJar task is responsible for creating the executable jar file. This is created automatically once the java plugin is applied.
In case the executable jar/war file is not generated run the below gradle task manually.
$./gradlew bootJar
Similarly, bootWar generates an executable war file and gets created once the war plugin is applied.
We can execute the bootWar task using:
$./gradlew bootWar
Note that for Spring Boot 2.x, we need to use Gradle 4.0 or later.
I created a project with all the sources you provided. Running "gradle build" from terminal, switching to /build/libs and then running "java -jar artifactname" works just fine.
Have you tried to clean and recompile? Which Version of Gradle are you using?
In spring boot you can directly create executable jar file by
springBoot {
executable = true
}
Please try
jar{
baseName = 'myapp'
version = 'version'
}
It will create jar with name myapp-version.jar
Do ./myapp-version.jar from command line.it will execute
Refer following link for more info. https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html
I just recently tried a Spring boot application with 2.1.4.Release with Gradle build.
I ran the following command from the directory in Windows CMD.
gradlew clean build
(upon required JDK8 installed in the system), I was able to see the JAR generated under,
<project-directory>/build/libs/<project-name-version.jar>
Hope this helps though older question.
Reference:
My two cents.
When using spring-boot if you want to customize the MANIFEST.MF file, you need to set the bootJar task, it won't work on the default jar task.
bootJar {
manifest {
attributes 'Start-Class': 'com.baeldung.DemoApplication'
}
}
https://www.baeldung.com/spring-boot-main-class
If you're trying to make your .jar file executable, for use such as in a systemd service. You'll have to edit the bootJar task and enable launchScript.
build.gradle
bootJar {
launchScript()
}
or with Gradle Kotlin DSL build.gradle.kts
tasks {
bootJar {
launchScript()
}
}
You should now be able to run your project's .jar file as an executable.

Spring Boot Gradle build fails with "Execution failed for task 'bootWarMainClassName'

I have a multi-project gradle build with Spring Boot structured per default gradle conventions.
root
-- common
-- src/main/java
-- bootproject
-- src/main/java
My current project is to (A) upgrade gradle from 5.x to 7.3.x and (B) use embedded tomcat with Spring Boot.
This is a project that has existed for many years and is Spring Boot but has always been deployed traditionally as a WAR file in Tomcat.
I have upgraded gradle to 7.3.3 following the gradle migration guide and have "common" building correctly (java-library). I am now trying to make "bootproject" build correctly again. I have migrated my build.gradle, and compilation happens correctly now but upon executing 'gradlew sub-project:build' I get the error:
Execution failed for task ':tx-main:bootWarMainClassName'.
java.lang.IllegalArgumentException (no error message)
My ROOT build.gradle is simple:
plugins {
id "org.springframework.boot" version "2.6.3"
}
subprojects {
apply plugin: 'java'
group = 'com.blah'
version = '2.1.1'
repositories {
mavenCentral()
}
There is a library sub-project (common) that builds fine.
The Spring Boot subproject build.gradle is:
plugins {
id 'org.springframework.boot'
id 'io.spring.dependency-management'
}
dependencies {
implementation project(path:':common', configuration:'default')
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation ...
}
The main class was originally set up to make the app conventionally deployed (extends SpringBootServletInitializer), but has been replaced with (taken directly from docs):
package com.blah;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The compilation works, this class appears to be in the build/classpath so I don't understand why Gradle-Spring-Boot is not finding it. I have also tried explicitly identifying the class with the same result.
Unfortunately I find little documentation about multi-project gradle builds so I suspect that is part of the problem. Hopefully someone here can point me in the right direction as to what is wrong.
Thanks
I discovered the issue here. We have custom code in /buildSrc that is apparently causing this.

can't use 'springBoot' scope in kotlin build script

I have a question.
There is a question about spring-boot-gradle-plugin. I written about spring boot example that allows the specified version (ex. 2.2.1.RELEASE).
By the way, springBoot scope is not available when applying spring-boot-gradle-plugin written in legacy plugin application style. But the plugin DSL style is not a problem.
I know that there is no dynamic version assignment in the plugins scope in Kotlin build scripts, so I have to write in the legacy plugin application style.
The code is as follows:
Using legacy plugin application:
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.2.1.RELEASE")
}
}
apply(plugin = "org.springframework.boot")
springBoot {
mainClassName = "blabla~~"
}
e: .../build.gradle.kts:25:1: Unresolved reference: springBoot
Using the plugins DSL:
plugins {
id("org.springframework.boot") version "2.2.1.RELEASE"
}
springBoot {
mainClassName = "blabla~~"
}
no problem
Thanks! 😄

SpringBoot WAR file deployed into Tomcat running but still returning 404

I want to demonstrate a simple SpringBoot application being deployed into a separately running Tomcat instance. Even though my application appears to have been deployed, I cannot access it in Tomcat.
I have followed the 'Traditional deployment' instructions in the Spring documentation.
I have tried using a different version of Tomcat and running the war file as ROOT.warbut get similar behaviour.
When I run the war standalone e.g. java -jar build/libs/springbootify.war I can successfully access the expected URL http://localhost:8080/doit.
Application.java
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(Application.class);
}
}
MyController.java
#RestController
public class MyController {
#GetMapping(value = "/doit")
public String getSomething() {
return "Something";
}
}
build.gradle
plugins {
id 'war'
id 'java'
id 'io.spring.dependency-management' version "1.0.8.RELEASE"
id 'org.springframework.boot' version '2.1.6.RELEASE'
id 'com.palantir.docker' version '0.22.1'
id 'com.palantir.docker-run' version '0.22.1'
}
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
compile 'org.springframework.boot:spring-boot-devtools'
}
See the reproducible example on Github.
I can see that the war file has been deployed in the Tomcat logs
29-Jun-2019 17:07:04.434 INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/springbootify.war] has finished in [857] ms
However, when I access http://localhost:8080/springbootify/doit I get a 404 response.
I expect this to return the string "something" with a 200 response.
UPDATE - 2019-07-12
I have tried building the war using Maven, and manually deploying to Tomcat via the manager UI and this works correctly
Inspecting the difference between the war files generated by Gradle and Maven I found the following:
1) Maven adds these additional attributes to the META-INF/MANIFEST.MF:
Created-By: Maven Archiver 3.4.0
Build-Jdk-Spec: 12
Implementation-Title: springbootify
Implementation-Version: 0.0.1-SNAPSHOT
2) Maven adds this additional library to the war:
WEB-INF/lib/javax.annotation-api-1.3.2.jar whereas in the Gradle war it is in WEB-INF/lib-provided/javax.annotation-api-1.3.2.jar
I'm going to suppose it's one of these reasons that it's not working with Gradle, but so far I haven't managed to modify the Gradle generated war to include these differences.
The newly added pom.xml can also be viewed on the provided Github link.

Spring Boot not updating static html (with Kotlin/Intellij)

I built a pretty simple static content web server using Spring Boot, Kotlin and Intellij. I run my program and it properly serves my html on localhost:8080. However, if I update that html while the server is running then force reload the page in the browser my changes are never shown. I've read a bunch of things that could be the problem. Something about thymeleaf cache. Spring Boot Devtools. Intellij auto builds or something. Nothing seems to work. Maybe an extra set of eyes on my code could pick up something I'm missing.
Here's my build.gradle
buildscript {
ext.kotlin_version = "1.2.0"
ext.httpcomponents_version = "4.5.4"
ext.springframework_version = "1.5.9.RELEASE"
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springframework_version"
}
}
group "com.atomicslurry"
version "1.0"
apply plugin: "java"
apply plugin: "kotlin"
apply plugin: "spring-boot"
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.apache.httpcomponents:httpclient:$httpcomponents_version"
compile "org.apache.httpcomponents:fluent-hc:$httpcomponents_version"
compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.2"
compile "org.springframework.boot:spring-boot-starter-thymeleaf:$springframework_version"
compile "org.springframework.boot:spring-boot-devtools:$springframework_version"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
Here's my kotlin app main
package com.atomicslurry.sentinel
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
open class App
fun main(args: Array<String>) {
SpringApplication.run(App::class.java, *args)
}
Finally, my static html is in src/main/resources/static
Any ideas?
The general workflow that is working for me:
Edit HTML
Press Build Project (Ctr+F9) in IntelliJ, then wait until changes completed
Go to Browser and refresh
add this lines to application.properties
spring.web.resources.static-locations[0]=file:./src/main/resources/public/
spring.web.resources.static-locations[1]=classpath:/public/
If you're using Spring Boot with static resources at src/main/resources/public, this can be done by add the following to application.properties:
spring.web.resources.static-locations[0]=file:src/main/resources/public/
spring.web.resources.static-locations[1]=classpath:/public/
The "file:" makes the content update on refreshing the browser,
see related issue.
Alternatively, the file resource locations can be discovered at runtime and added programmatically.
See also documentation and tutorial.

Resources