Spring Boot + Gradle: how to build executable jar - java

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.

Related

ClassNotFoundException while running gradle based Java Application in cmd

I have a Simple Gradle based Java project, After gradle build jar has been created under build/libs folder. When i try to run in cmd using java -jar discord-notification-bot-1.0-SNAPSHOT.jar in the below path
F:\github projects\discord-notification-bot\build\libs>
I'm getting below Error
Exception in thread "main" java.lang.NoClassDefFoundError: org/javacord/api/DiscordApiBuilder
at Application.main(Application.java:8)
Caused by: java.lang.ClassNotFoundException: org.javacord.api.DiscordApiBuilder
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 1 more
build.gradle
plugins {
id 'java'
}
group 'org.test'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.javacord:javacord:3.0.6'
}
jar {
manifest {
attributes(
'Main-Class': 'Application'
)
}
}
under src/Application.java
import org.javacord.api.DiscordApi;
import org.javacord.api.DiscordApiBuilder;
public class Application {
public static void main(String[] args) {
String key = System.getenv().get("key");
DiscordApi api = new DiscordApiBuilder().setToken(key).login().join();
System.out.println("you can invite the bot by using following url "+api.createBotInvite());
}
}
It seems javacord library is not getting reflected while i run via cmd.
Note: in MANIFEST.MF I have main application correctly.
But dependency is not getting reflected while running via cmd.
So, What should I do for that?
The gradle java plugin will not add classpath information to the generated JARs by default. You could add this information similar to the way you added the main class but you're probably better off using the gradle application plugin.
With the application plugin you'll get an archive file as output that bundles all dependencies and some bat/sh files as well for execution. A simple gradle file with the application plugin might look like this:
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}
repositories {
// Use JCenter for resolving dependencies.
jcenter()
}
dependencies {
// This dependency is used by the application.
implementation 'com.google.guava:guava:29.0-jre'
}
application {
// Define the main class for the application.
mainClass = 'gradle.application.App'
}
As mentioned in the comments another solution might be to create a shadow/fat/uber-jar using the com.github.johnrengelman.shadow plugin. Which will result in this basic gradle file:
plugins {
id 'java'
// Apply the shadow plugin to add support for building a shadow-jar
id "com.github.johnrengelman.shadow" version "6.1.0"
}
repositories {
// Use JCenter for resolving dependencies.
jcenter()
}
dependencies {
// This dependency is used by the application.
implementation 'com.google.guava:guava:29.0-jre'
}
jar {
manifest {
attributes(
'Main-Class': 'Application'
)
}
}
With the shadow-plugin you get an additional task shadowJar that will create a JAR with all dependencies included. While this sounds very handy, it comes with some drawbacks e.g. when using the shadow-jar as dependency itself, which might result in the need of additional configuration. In your case however, this might be the best option.

Gradle - cannot load a Java Class even when packed within the jar

My gradle build file is
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building a CLI application
id 'application'
}
apply plugin: 'java'
jar {
from configurations.runtime
manifest {
attributes(
'Created-By':'Gmack',
'Main-Class':'myapprunner.App',
'Class-Path':'mydaos-1.0.jar'
)
}
}
allprojects{
repositories {
jcenter()
}
}
subprojects {
version = '1.0'
apply plugin: 'java'
}
dependencies {
// This dependency is used by the application.
implementation 'com.google.guava:guava:27.1-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
// Compile Project for dependency
compile project(':mydaos')
}
application {
// Define the main class for the application
mainClassName = 'myapprunner.App'
}
When I run the app using java -jar myapprunner.jar
I get a ClassNotFoundException
Caused by: java.lang.ClassNotFoundException: com.mydaos.Library
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
I can confirm that the jar has been packed. Not sure why this is not picking things up.
Any help would be appreciated.
Thanks,
Plugin java is being applied twice and com.mydaos.Library is likely being pulled in from compile project(':mydaos') (or 'Class-Path':'mydaos-1.0.jar'). Would assume the project does not build or the class path is wrong.
Dependency classes (projects/external jars) aren't packed inside your jar by default.
You are using the application plugin which bundles your classes, your dependencies and an execution script in a zip so you should use that. The plugin also adds a "run" task to your project to run your main class via gradle for development purposes. See the application plugin docs for more info
If you want to pack your dependencies inside your jar (known as an uber jar) see here. I suggest you stop using the application plugin if you do this
'Class-Path':'mydaos-1.0.jar'
This assumes that mydaos-1.0.jar is in the same folder you are running java -jar ... from which is likely not the case

Tasks for build and run works but executing with the generated run script doesn't

I am doing the simple HelloWorld example from https://spring.io/guides/gs/gradle/.
I had to do some changes (I'm using Gradle 5.2.1 on Ubuntu 18) to the build.gradle. I used gradlew wrapper. I managed to get tasks like 'build' and 'run' working. Everything is generated correctly, it seems. But running the app without gradle using the generated build/scripts/<appscript> does not work. Running the jar with
java -jar build/libs/hello-1.0.jar
works. But
./build/scripts/sayhello
Does not work and produces an error:
erno#moongate:~/Projects/java/sayhello$ ./build/scripts/sayhello
Error: Could not find or load main class hello.HelloWorld
Caused by: java.lang.ClassNotFoundException: hello.HelloWorld
Project file structure is as suggested:
sayhello/
build.gradle
gradlew
src/
main/
java/
hello/
Greeter.java
HelloWorld.java
I had to add the manifest and the mainclass attribute to the build configuration file as it seems that the gradle init --type java-application does not do it. Meaning that even trying to run the gradle generated base project does not work.
My build.gradle is like this:
plugins {
id 'java'
id 'application'
}
mainClassName = 'hello.HelloWorld'
repositories {
mavenCentral()
jcenter()
}
dependencies {
compile "joda-time:joda-time:2.10"
testCompile "junit:junit:4.12"
}
jar {
manifest {
attributes(
'Main-Class': 'hello.HelloWorld'
)
}
baseName = 'hello'
version = '1.0'
}
The problem with the startScripts task is that it generates a very basic script. It does not make sure dependent jars are in the right places - it expects this to be done by you. Also it assumes that you will be running the script from a directory it refers to as the $APP_HOME and this folder needs to contain a lib folder which contains all the jars your app needs.
My very hacky solution is to generate an even more basic unix script instead of relying on the default one.
startScripts {
dependsOn jar
doFirst {
unixStartScriptGenerator = configure(new CustomUnixStartScript()) {
classpath = configurations.runtimeClasspath + jar.outputs.files
}
}
}
class CustomUnixStartScript implements ScriptGenerator {
#InputFiles
FileCollection classpath
#Override
void generateScript (JavaAppStartScriptGenerationDetails details, Writer destination) {
destination << """java -classpath $classpath.asPath ${details.mainClassName}"""
}
}
You can extend this as you see fit.

Gradle - Change the name of the jar

I would like to change the name of the jar when I do a
./gradlew clean assemble
The name of my jar is my-awsome-app-0.0.1-SNAPSHOT.jar and I want to generate my-awsome-app.jar.
I tested to do this in the build.gradle:
configurations {
jar.archiveName = "my-awsome-app.${jar.extension}"
}
...but this is not working, the name is still the same
Since you are using Spring Boot, you should use the bootJar task instead (notice bootJar extends Jar).
Since archiveName in Jar has been deprecated, use archiveFileName:
bootJar {
archiveFileName = 'my-awesome-app.jar'
}

How to export a Spring Boot project to an executable standalone file?

I have created a project in Spring Tool Suite with Spring Boot and Gradle, and I really don't know how to export to make it work.
I don't know much about gradle, just the basics to add dependencies from the maven repository. So in some articles says to apply the application plugin to do the task, but I don't know how to set up the configuration file and how to create the executable.
If anyone could write or link a step by step detailed explanation on how to do it, it would be very much appreciated.
This is my build.gradle file:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.1.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'
springBoot {
mainClass = "com.rodamientosbulnes.objetivosventa.Application"
executable = true
}
jar {
baseName = 'objetivosventa'
version = '0.1.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework:spring-jdbc')
compile('net.sourceforge.jtds:jtds:1.3.1')
compile('org.apache.poi:poi-ooxml:3.13')
compile('com.miglayout:miglayout-swing:4.2')
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
Build file looks fine, you only need to run gradle build (or Run As -> Gradle -> Gradle build in the STS) to create the runnable jar.
More details about configuration of the gradle plugin are available on spring boot documentation site.
Gradle's application plugin doesn't make a single execitable for you, but it can create a distribution, which includes all the dependencies, jar-artifact for your project and 2 scripts to run it (one batch-file and linex executable).
The main thing you need to know, is that spring-boot plugin already provide all the task from application plugin you may need. All the task you can find here. You need distZip or installDist to package your project to the distribution. This task will create a ready project distribution under your project-folder/build folder. One more task you may find usefull is buildRun which will run you spring-boot application without package it into distribution.

Categories

Resources