2

I created a simple Spring Boot application using start.spring.io with Maven, Java 19, Spring Boot 3.0

I added just one additional dependency, commons-lang3

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>19</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

When NetBeans 15 builds the project, there is a target folder containing the jar file demo-0.0.1-SNAPSHOT.jar

I can run that jar with

java -jar target/demo-0.0.1-SNAPSHOT.jar

I can see my output and the Spring Boot logo and it terminates.

java -jar target/demo-0.0.1-SNAPSHOT.jar 

MIX

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.0.0)

2022-11-30T19:47:53.468+01:00  INFO 18179 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication v0.0.1-SNAPSHOT using Java 19.0.1 with PID 18179 (/home/me/NetBeansProjects/demo/target/demo-0.0.1-SNAPSHOT.jar started by neblaz in /home/me/NetBeansProjects/demo)
2022-11-30T19:47:53.470+01:00  INFO 18179 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2022-11-30T19:47:54.131+01:00  INFO 18179 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 1.172 seconds (process running for 1.666)

The main class is simple

package com.example.demo;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        String mix = "MIX";
        if (StringUtils.isNoneBlank(mix)) {
            System.out.println(mix);
        }
        SpringApplication.run(DemoApplication.class, args);
    }

}

Now I want to create a minimized custom runtime image with jlink to run that jar file with it.

I found this article https://medium.com/azulsystems/using-jlink-to-build-java-runtimes-for-non-modular-applications-9568c5e70ef4 but cannot figure it out.

With jdeps I get some not fully understandable output

jdeps target/demo-0.0.1-SNAPSHOT.jar
demo-0.0.1-SNAPSHOT.jar -> java.base
demo-0.0.1-SNAPSHOT.jar -> java.logging
demo-0.0.1-SNAPSHOT.jar -> not found
   com.example.demo                                   -> java.io                                            java.base
   com.example.demo                                   -> java.lang                                          java.base
   com.example.demo                                   -> org.apache.commons.lang3                           not found
   com.example.demo                                   -> org.springframework.boot                           not found
   com.example.demo                                   -> org.springframework.boot.autoconfigure             not found
   com.example.demo                                   -> org.springframework.context                        not found
   org.springframework.boot.loader                    -> java.io                                            java.base
   org.springframework.boot.loader                    -> java.lang                                          java.base
   org.springframework.boot.loader                    -> java.lang.invoke                                   java.base
   org.springframework.boot.loader                    -> java.lang.reflect                                  java.base
   org.springframework.boot.loader                    -> java.net                                           java.base
   org.springframework.boot.loader                    -> java.nio.charset                                   java.base
   org.springframework.boot.loader                    -> java.security                                      java.base
   org.springframework.boot.loader                    -> java.util                                          java.base
   org.springframework.boot.loader                    -> java.util.function                                 java.base
   org.springframework.boot.loader                    -> java.util.jar                                      java.base
   org.springframework.boot.loader                    -> java.util.regex                                    java.base
   org.springframework.boot.loader                    -> java.util.stream                                   java.base
   org.springframework.boot.loader                    -> java.util.zip                                      java.base
   org.springframework.boot.loader                    -> org.springframework.boot.loader.archive            demo-0.0.1-SNAPSHOT.jar
   org.springframework.boot.loader                    -> org.springframework.boot.loader.jar                demo-0.0.1-SNAPSHOT.jar
   org.springframework.boot.loader                    -> org.springframework.boot.loader.util               demo-0.0.1-SNAPSHOT.jar
   org.springframework.boot.loader.archive            -> java.io                                            java.base
   org.springframework.boot.loader.archive            -> java.lang                                          java.base
   org.springframework.boot.loader.archive            -> java.lang.invoke                                   java.base
   org.springframework.boot.loader.archive            -> java.net                                           java.base
   org.springframework.boot.loader.archive            -> java.nio.file                                      java.base
   org.springframework.boot.loader.archive            -> java.nio.file.attribute                            java.base
   org.springframework.boot.loader.archive            -> java.util                                          java.base
   org.springframework.boot.loader.archive            -> java.util.function                                 java.base
   org.springframework.boot.loader.archive            -> java.util.jar                                      java.base
   org.springframework.boot.loader.archive            -> java.util.zip                                      java.base
   org.springframework.boot.loader.archive            -> org.springframework.boot.loader.jar                demo-0.0.1-SNAPSHOT.jar
   org.springframework.boot.loader.data               -> java.io                                            java.base
   org.springframework.boot.loader.data               -> java.lang                                          java.base
   org.springframework.boot.loader.jar                -> java.io                                            java.base
   org.springframework.boot.loader.jar                -> java.lang                                          java.base
   org.springframework.boot.loader.jar                -> java.lang.invoke                                   java.base
   org.springframework.boot.loader.jar                -> java.lang.ref                                      java.base
   org.springframework.boot.loader.jar                -> java.lang.reflect                                  java.base
   org.springframework.boot.loader.jar                -> java.net                                           java.base
   org.springframework.boot.loader.jar                -> java.nio.charset                                   java.base
   org.springframework.boot.loader.jar                -> java.security                                      java.base
   org.springframework.boot.loader.jar                -> java.security.cert                                 java.base
   org.springframework.boot.loader.jar                -> java.time                                          java.base
   org.springframework.boot.loader.jar                -> java.time.temporal                                 java.base
   org.springframework.boot.loader.jar                -> java.util                                          java.base
   org.springframework.boot.loader.jar                -> java.util.concurrent                               java.base
   org.springframework.boot.loader.jar                -> java.util.function                                 java.base
   org.springframework.boot.loader.jar                -> java.util.jar                                      java.base
   org.springframework.boot.loader.jar                -> java.util.logging                                  java.logging
   org.springframework.boot.loader.jar                -> java.util.regex                                    java.base
   org.springframework.boot.loader.jar                -> java.util.stream                                   java.base
   org.springframework.boot.loader.jar                -> java.util.zip                                      java.base
   org.springframework.boot.loader.jar                -> org.springframework.boot.loader.data               demo-0.0.1-SNAPSHOT.jar
   org.springframework.boot.loader.jarmode            -> java.io                                            java.base
   org.springframework.boot.loader.jarmode            -> java.lang                                          java.base
   org.springframework.boot.loader.jarmode            -> java.lang.invoke                                   java.base
   org.springframework.boot.loader.jarmode            -> java.util                                          java.base
   org.springframework.boot.loader.jarmode            -> org.springframework.core.io.support                not found
   org.springframework.boot.loader.jarmode            -> org.springframework.util                           not found
   org.springframework.boot.loader.util               -> java.io                                            java.base
   org.springframework.boot.loader.util               -> java.lang                                          java.base
   org.springframework.boot.loader.util               -> java.lang.invoke                                   java.base
   org.springframework.boot.loader.util               -> java.util                                          java.base

I understand that the application uses the Java modules java.base and java.logging, but sometimes not found and sometimes the jar file are give as modules.

Trying jlink like

jlink --module-path $JAVA_HOME/jmods --add-modules java.base,java.logging --output mycustomrt

creates the custom runtime image, but running it with

mycustomrt/bin/java -jar target/demo-0.0.1-SNAPSHOT.jar

results in

19:08:07.912 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.NoClassDefFoundError: java/beans/PropertyEditorSupport
        at java.base/java.lang.ClassLoader.defineClass1(Native Method)
        at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1013)
        at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
        at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:524)
        at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:427)
        at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:421)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:712)
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:420)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:149)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at org.springframework.boot.context.properties.bind.BindConverter$TypeConverterConverter.<clinit>(BindConverter.java:180)
        at org.springframework.boot.context.properties.bind.BindConverter$TypeConverterConversionService.<init>(BindConverter.java:157)
        at org.springframework.boot.context.properties.bind.BindConverter.<init>(BindConverter.java:63)
        at org.springframework.boot.context.properties.bind.BindConverter.getSharedInstance(BindConverter.java:133)
        at org.springframework.boot.context.properties.bind.BindConverter.get(BindConverter.java:126)
        at org.springframework.boot.context.properties.bind.Binder.<init>(Binder.java:190)
        at org.springframework.boot.context.properties.bind.Binder.<init>(Binder.java:160)
        at org.springframework.boot.context.properties.bind.Binder.<init>(Binder.java:139)
        at org.springframework.boot.context.properties.bind.Binder.get(Binder.java:531)
        at org.springframework.boot.context.properties.bind.Binder.get(Binder.java:516)
        at org.springframework.boot.context.config.ConfigDataEnvironment.<init>(ConfigDataEnvironment.java:141)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.getConfigDataEnvironment(ConfigDataEnvironmentPostProcessor.java:101)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:96)
        at org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor.postProcessEnvironment(ConfigDataEnvironmentPostProcessor.java:89)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:109)
        at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:94)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
        at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
        at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:81)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:64)
        at java.base/java.lang.Iterable.forEach(Iterable.java:75)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:112)
        at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:63)
        at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:352)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)
        at com.example.demo.DemoApplication.main(DemoApplication.java:18)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
        at java.base/java.lang.reflect.Method.invoke(Method.java:578)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:95)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65)
Caused by: java.lang.ClassNotFoundException: java.beans.PropertyEditorSupport
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:149)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        ... 49 common frames omitted

The jlink command seems not to be fully correct, don't know how to specify it correctly.

Looking into the demo-0.0.1-SNAPSHOT.jar file I see that all necessary jars are

BOOT-INF
   classpath.idx
   layers.idx
   lib/commons-lang3-3.12.0.jar
   lib/spring-boot-3.0.0.jar
   lib/...

1 Answer 1

2

Can you try with adding this as vm argument :

--add-modules java.xml,java.sql,java.prefs,java.desktop
Sign up to request clarification or add additional context in comments.

3 Comments

jlink --module-path $JAVA_HOME/jmods --add-modules java.base,java.logging,java.xml,java.sql,java.prefs,java.desktop --output mycustomrt and with mycustomrt/bin/java -jar target/demo-0.0.1-SNAPSHOT.jar it runs. Thanks! But can you explain why adding specificly these Java modules does it? I cannot put these in relation to the output of the jdeps command. Just addding those you mentioned is also enough, java.base and java.logging don't have to be added.
Interestingly this is enough too: jlink --module-path $JAVA_HOME/jmods --add-modules java.desktop --output mycustomrt I would like to know why just this is enough. Especially I would like to know how to figure it out which modules to add when looking at the jdeps output, or can it be figured out somehow better?
Did you manage to get the list of modules to be included in customized JRE from 'jdeps' to be able successfully run your springboot app ?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.