The focus of this article is to build a Native Executable Reactive CRUD RESTful API built using GraalVM + Spring Boot.
This is an extension to the previously published article -
The core principles of implementing Reactive CRUD RESTful APIs remain the same to the
GraalVM
is a high-performance runtime that provides significant improvements in application performance and efficiency which is ideal for microservices. It is a Java VM and JDK based on HotSpot/OpenJDK, implemented in Java. It supports additional programming languages.
GraalVM
provides benefits by running Java Applications faster by creating ahead-of-time compiled native (system-dependent) machine code resulting the binary image to be executed natively.
Running Java application inside a JVM comes with startup and footprint costs. GraalVM has a feature to create native images for existing Java applications enabling faster startup and utilizing low memory footprint.
The image generation process employs static analysis to find any code reachable from the main Java method and then performs full ahead-of-time (AOT) compilation. The resulting native binary contains the whole program in machine code form for its immediate execution.
For more deep drive understanding, jump to
GraalVM
is available as GraalVM Community and GraalVM Enterprise editions.
For the scope of this article, we shall see on installing GraalVM Community on Ubuntu.
π Refer to
Ubuntu 19.10
along side with Windows 10
and setup your environment for Java Development
.Follow below series of steps to install GraalVM 20.2.0 for Java 11:
$ sudo apt-get update -y
$ sudo apt-get install -y libz-dev
$ cd /home/<user>/Downloads
$ wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.2.0/graalvm-ce-java11-linux-amd64-20.2.0.tar.gz
$ tar -xvzf graalvm-ce-java11-linux-amd64-20.2.0.tar.gz
$ sudo mv graalvm-ce-java11-20.2.0/ /usr/lib/jvm/
$ sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/bin/java 1
$ java -version
$ vi ~/.bashrc
export JAVA_HOME=/usr/lib/jvm/
export PATH=${PATH}:${JAVA_HOME}/bin
export GRAALVM_HOME=/usr/lib/jvm/
$ cd /home/<user>/Downloads
$ wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-20.2.0/native-image-installable-svm-java11-linux-amd64-20.2.0.jar
$ gu -L install native-image-installable-svm-java11-linux-amd64-20.0.0.jar
$ gu list
$ mkdir /home/<user>/learning/java
$ vi HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
$ javac HelloWorld.java
$ native-image HelloWorld
$ ./helloworld
π π You are all set if you see Hello, World!
printed in the console. π π
As mentioned above, this article is an extension to the previously published article -
The core application remains the same with minor changes to support building native executable using GraalVM.
Compared to the previous article, libraries are uplifted to their latest versions for better support with GraalVM 20.2.0
2.3.3.RELEASE
to 2.4.0-M2
.spring-r2dbc
module coexisting with spring-data-r2dbc
.In favor of Spring R2DBC
module introduced as part of Spring Framework v5.3.0-M2
, changes are done to Spring Data R2DBC v1.2
to deprecate classes from org.springframework.data.r2dbc.connectionfactory.init
package which are used for initializing database connection.
Rather, we should be using classes from Spring R2DBC
under package org.springframework.r2dbc.connection.init
to initialize io.r2dbc.spi.ConnectionFactory
as below.
package com.toomuch2learn.reactive.nativebuild.catalogue.crud.configuration;
import io.r2dbc.spi.ConnectionFactory;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.r2dbc.connection.init.CompositeDatabasePopulator;import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer;import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator;
/**
* Class to initialize any configurations or beans needed for application
*
* @author Madan Narra
*/
@Configuration(proxyBeanMethods=false)
public class ApplicationConfiguration {
/**
* When using R2DBC, there is no support in Spring Boot to for initialising a database using schema.sql or data.sql.
*
* Database cannot be initialized with schema or seed data by annotating the configuration class with
* @EnableAutoConfiguration or by specifying initialization-mode config param.
*
* @param connectionFactory
* @return connectionFactoryInitializer
*/
@Bean
public ConnectionFactoryInitializer databaseInitializer(ConnectionFactory connectionFactory) {
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer(); initializer.setConnectionFactory(connectionFactory);
CompositeDatabasePopulator populator = new CompositeDatabasePopulator(); populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema/schema.sql"))); populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema/data.sql")));
initializer.setDatabasePopulator(populator);
return initializer;
}
}
Spring GraalVM Native a.k.a spring-graalvm-native
is an experimental project introduced to support building Spring Boot native applications using GraalVM.
GraalVM Native Image Builder a.k.a native-image
is a utility that processes all the classes of our application and their dependencies, including those from the JDK. It analyses these classes to determine which classes
, methods
and fields
are reachable during application execution. It then ahead-of-time compiles all reachable code and data into a native executable for a specific operating system and architecture. This entire process is called image build time to clearly distinguish it from the compilation of Java source code to bytecode.
When creating the image using native-image
building tool, spring-graalvm-native
will source the information about the application, for example what resources are being loaded, what types might be getting reflected upon and whether types can be safely initialized as the image is built or must be initialized later at runtime. This information enables the native-image
tool to try and build an optimal image for the application.
Add the below dependency to pom.xml to start take advantage of analyzing the application and support building native executable.
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-graalvm-native</artifactId>
<version>0.8.0</version>
</dependency>
The only kind of proxy allowed with native images
is a JDK proxy
. It is not possible to use CGLIB
or some other kind of generated proxy. To compile the application to a native executable, our application needs to switch to using proxyBeanMethods=false
on @SpringBootApplication
and @Configuration
.
proxyBeanMethods
specifies whether @Bean
methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct @Bean method calls in user code.
The default is true
meaning each @Bean
method will get proxied through CgLib
. Each call to the method will pass through the proxy and assuming singleton scoped beans, it will return the same instance each time the method is called.
When setting it to false
no such proxy method will be created and each call to the method will create a new instance of the bean. It will act just as a factory method.
proxyBeanMethods=false
is marked in the below classes which are annotated with @SpringBootApplication
and @Configuration
.
@SpringBootApplication(proxyBeanMethods=false)@EnableConfigurationProperties({
FileStorageProperties.class
})
public class SpringReactiveNativeCatalogueCrudApplication {
public static void main(String[] args) {
SpringApplication.run(SpringReactiveNativeCatalogueCrudApplication.class, args);
}
}
@Configuration(proxyBeanMethods=false)public class ApplicationConfiguration {
@Bean
public ConnectionFactoryInitializer databaseInitializer(ConnectionFactory connectionFactory) {}
}
@Data
@Configuration(proxyBeanMethods = false)@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadLocation;
}
To successfully generate a native executable, native-image
builder needs couple of configuration files to register proxies, reflections,resources.
Having these hand written from scratch is near impossible. To the rescue comes the native-image-agent
tracing agent that produces these configuration files by tracing the classes for their names, methods and types when ran in the Java Hotspot VM.
native-image
builder will automatically picks up configuration files from META-INF/native-image
folder.
So, its ideal to create folder META-INF/native-image
under resources
and have these files generated when the application is launched with the tracing agent.
Application has to be thoroughly exercised by executing all possible use cases, so every nook and corner of the application classes are analyzed and included in the configuration classes.
$ mvn -DskipTests=true clean package
$ export MI=src/main/resources/META-INF
$ mkdir -p $MI
$ java \
-agentlib:native-image-agent=config-output-dir=${MI}/native-image \
-jar target/spring-reactive-native-catalogue-crud-0.0.1-SNAPSHOT.jar
This option is certainly quick but rather manual/tedious.
The other way is to run application tests to exercise the application. This option sounds much more appealing for a robust/repeatable setup but by default the generated configuration will include anything required by the test infrastructure, which is unnecessary when the application runs for real.
To address this problem the agent supports an access-filter.json
file that will cause certain data to be excluded from the generated output.
Include the below to resources\access-filter.json
and thus classes under these registered packages will not be included in the native executable.
{ "rules": [
{"excludeClasses": "org.apache.maven.surefire.**"},
{"excludeClasses": "net.bytebuddy.**"},
{"excludeClasses": "org.apiguardian.**"},
{"excludeClasses": "org.junit.**"},
{"excludeClasses": "org.mockito.**"},
{"excludeClasses": "org.springframework.test.**"},
{"excludeClasses": "org.springframework.boot.test.**"},
{"excludeClasses": "reactor.test.**"},
{"excludeClasses": "com.toomuch2learn.reactive.nativebuild.catalogue.crud.test.**"}
]
}
As mentioned earlier, couple of dependencies are uplifted to their latest version and few new dependencies are added to support creating native executable.
Apart from the dependencies uplift, couple of changes need to be done to pom.xml
native
profile is introduced with all applicable plugins registered and to be used as part of native executable build process.maven-surefire-plugin
with tracing agent arguments which will be tagged when executing test classes.maven-antrun-plugin
to create META_INF\native-image
folder to hold the configuration files created by the tracing agent.native-image-maven-plugin
is to be configured which will help to create the native executable using the native-image
configuration filesspring-boot-maven-plugin
with native-image build argumentsBelow is the complete pom.xml defined with all the dependencies and plugins needed for this application.
Have close eye at the highlighted code which might need some special interest.
<?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>2.4.0-M2</version> <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.toomuch2learn</groupId>
<artifactId>spring-reactive-native-catalogue-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-reactive-native-catalogue-crud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<start-class>
com.toomuch2learn.reactive.nativebuild.catalogue.crud.SpringReactiveNativeCatalogueCrudApplication </start-class>
<io.r2dbc.version>0.8.3.RELEASE</io.r2dbc.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-r2dbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-r2dbc</artifactId> </dependency>
<!--
https://stackoverflow.com/questions/62358435/graalvm-native-image-for-springboot-fat-jar-throws-nosuchmethodexception-xxx-in
https://stackoverflow.com/questions/50911552/graalvm-and-spring-applications/61680146#61680146
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-spi</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-pool</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-h2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.25</version>
</dependency>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-graalvm-native</artifactId>
<version>0.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <image> <name>spring-reactive-native-app</name> <builder>paketobuildpacks/builder:tiny</builder> <env> <BP_BOOT_NATIVE_IMAGE>1</BP_BOOT_NATIVE_IMAGE> <BP_BOOT_NATIVE_IMAGE_BUILD_ARGUMENTS> --no-server --no-fallback --allow-incomplete-classpath --report-unsupported-elements-at-runtime -H:+ReportExceptionStackTraces -H:+TraceClassInitialization -Dspring.native.verify=true -Dspring.graal.mode=initialization-only -Dspring.graal.dump-config=/tmp/computed-reflect-config.json -Dspring.graal.verbose=true -Dspring.graal.skip-logback=true --initialize-at-build-time=javax.el.MapELResolver --initialize-at-build-time=javax.el.ListELResolver --initialize-at-build-time=sun.instrument.InstrumentationImpl --initialize-at-build-time=io.r2dbc.spi.IsolationLevel,io.r2dbc.spi --initialize-at-build-time=io.r2dbc.spi.ConstantPool,io.r2dbc.spi.Assert,io.r2dbc.spi.ValidationDepth --initialize-at-build-time=org.reactivestreams.Publisher --initialize-at-build-time=com.toomuch2learn.reactive.nativebuild.catalogue.crud.crud.SpringReactiveNativeCatalogueCrudApplication --initialize-at-build-time=org.springframework.data.r2dbc.connectionfactory --initialize-at-run-time=org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils --initialize-at-run-time=io.netty.channel.unix.Socket --initialize-at-run-time=io.netty.channel.unix.IovArray --initialize-at-run-time=io.netty.channel.epoll.EpollEventLoop --initialize-at-run-time=io.netty.channel.unix.Errors </BP_BOOT_NATIVE_IMAGE_BUILD_ARGUMENTS> </env> </image> </configuration> </plugin> </plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>Maven Repo</id>
<name>Maven Repo</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
</pluginRepositories>
<profiles>
<profile>
<id>native</id> <build>
<plugins> <plugin> <artifactId>maven-antrun-plugin</artifactId> <executions> <execution> <id>make-native-image-config-folder</id> <phase>test-compile</phase> <configuration> <target> <mkdir dir="src/main/resources/META-INF/native-image"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <argLine>-Dorg.graalvm.nativeimage.imagecode=agent -Dspring.xml.ignore=true -Dspring.spel.ignore=true -agentlib:native-image-agent=access-filter-file=target/classes/access-filter.json,config-output-dir=src/main/resources/META-INF/native-image</argLine> </configuration> </plugin> <plugin> <groupId>org.graalvm.nativeimage</groupId> <artifactId>native-image-maven-plugin</artifactId> <version>20.2.0</version> <configuration> <mainClass>com.toomuch2learn.reactive.nativebuild.catalogue.crud.SpringReactiveNativeCatalogueCrudApplication</mainClass> <imageName>spring-reactive-native-app</imageName> <buildArgs> --no-server --no-fallback --allow-incomplete-classpath --report-unsupported-elements-at-runtime -H:+ReportExceptionStackTraces -H:+TraceClassInitialization -Dspring.native.verify=true -Dspring.graal.mode=initialization-only -Dspring.graal.dump-config=/tmp/computed-reflect-config.json -Dspring.graal.verbose=true -Dspring.graal.skip-logback=true --initialize-at-build-time=javax.el.MapELResolver --initialize-at-build-time=javax.el.ListELResolver --initialize-at-build-time=sun.instrument.InstrumentationImpl --initialize-at-build-time=io.r2dbc.spi.IsolationLevel,io.r2dbc.spi --initialize-at-build-time=io.r2dbc.spi.ConstantPool,io.r2dbc.spi.Assert,io.r2dbc.spi.ValidationDepth --initialize-at-build-time=org.reactivestreams.Publisher --initialize-at-build-time=com.toomuch2learn.reactive.nativebuild.catalogue.crud.crud.SpringReactiveNativeCatalogueCrudApplication --initialize-at-build-time=org.springframework.data.r2dbc.connectionfactory --initialize-at-run-time=org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils --initialize-at-run-time=io.netty.channel.unix.Socket --initialize-at-run-time=io.netty.channel.unix.IovArray --initialize-at-run-time=io.netty.channel.epoll.EpollEventLoop --initialize-at-run-time=io.netty.channel.unix.Errors </buildArgs> </configuration> <executions> <execution> <goals> <goal>native-image</goal> </goals> <phase>package</phase> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</profile>
</profiles>
</project>
π Unfortunatly, there is no Gradle support for this article at the moment as v2.4.0.M2 of βorg.springframework.bootβ is not yet available at
Section will be uplifted post gradle native-image plugin is available.
Below are series of steps that needs to be executed to build and package the application to run in JVM and Native mode.
JVM Mode is the regular version building the fat executable jar which can be deployed and executed on different architecture and platform.
$ mvn clean package
$ java -jar target/spring-reactive-native-catalogue-crud-0.0.1-SNAPSHOT.jar
Section will be uplifted post gradle native-image plugin is available.
Native Mode version is very specific to the architecture and platform on which the native executable build is created.
For GraalVM native-image
to build the native executable file, we need to pass certain build arguments which enables us to create the native executable file and also startup the application successfully.
--no-server
--no-fallback
--allow-incomplete-classpath
--report-unsupported-elements-at-runtime
-H:+ReportExceptionStackTraces
-H:+TraceClassInitialization
-Dspring.native.verify=true
-Dspring.graal.mode=initialization-only
-Dspring.graal.dump-config=/tmp/computed-reflect-config.json
-Dspring.graal.verbose=true
-Dspring.graal.skip-logback=true
--initialize-at-build-time=javax.el.MapELResolver
--initialize-at-build-time=javax.el.ListELResolver
--initialize-at-build-time=sun.instrument.InstrumentationImpl
--initialize-at-build-time=io.r2dbc.spi.IsolationLevel,io.r2dbc.spi
--initialize-at-build-time=io.r2dbc.spi.ConstantPool,io.r2dbc.spi.Assert,io.r2dbc.spi.ValidationDepth
--initialize-at-build-time=org.reactivestreams.Publisher
--initialize-at-build-time=com.toomuch2learn.reactive.nativebuild.catalogue.crud.crud.SpringReactiveNativeCatalogueCrudApplication
--initialize-at-build-time=org.springframework.data.r2dbc.connectionfactory
--initialize-at-run-time=org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils
--initialize-at-run-time=io.netty.channel.unix.Socket
--initialize-at-run-time=io.netty.channel.unix.IovArray
--initialize-at-run-time=io.netty.channel.epoll.EpollEventLoop
--initialize-at-run-time=io.netty.channel.unix.Errors
π Refer to
native-image
command line tool.π Refer to
initialize-at-build-time
& initialize-at-run-time
.$ mvn -Pnative clean package
$ ./target/spring-reactive-native-app
Section will be uplifted post gradle native-image plugin is available.
As observed in the above images with highlighted blocks, application is booted up and running in 5.286 Secs
in JVM
mode and in 0.528 Secs
in Native
mode.
π± π± π± That is around 90%
decrease in startup times π β¨ π₯
This definitely proves to run Java applications FASTER with native executable.
There are two possible approaches to containerize the application with the native executable build:
Using Spring Boot Plugin which uses Buildpacks under the hood
Using a custom Dockerfile
Underlying schematics of the base layer might differ, but the way the application boots and startup remains the same irrespective of the approach.
Spring Boot 2.3.0.M1
introduced simplified approach of creating docker images which uses
Run the below commands to create the docker image with the native executable build.
$ mvn clean spring-boot:build-image
Section will be uplifted post gradle native-image plugin is available.
π For more details, please refer to
Custom Dockerfile
is included in src/main/docker
which can be used as an alternative to create docker image with the native executable build.
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
WORKDIR /work/
COPY target/spring-reactive-native-app /work/application
# set up permissions for user `1001`
RUN chmod 775 /work /work/application \
&& chown -R 1001 /work \
&& chmod -R "g+rwX" /work \
&& chown -R 1001:root /work
EXPOSE 8080USER 1001
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
$ mvn -Pnative clean package
Section will be uplifted post gradle native-image plugin is available.
$ docker build -f src/main/docker/Dockerfile.native -t spring-reactive-native-app .
Either the way, docker image will be created with spring-reactive-native-app
name. But with the difference in the size of image and the timestamp when the image is created.
$ docker images
spring-reactive-native-app latest c84143c27e3b 40 years ago 142MB
β 40 Years when created with Spring Boot Plugin ?
This is an intentional feature for the purpose of reproducibility. Dig into
$ docker images
spring-reactive-native-app latest ef4d95e79361 25 seconds ago 347MB
$ docker run -it -p8080:8080 spring-reactive-native-app
This might be a very common one when getting started with GraalVM Native Executable builds.
If you are ever encountered with the below exception, then Run the application with native-image-agent connected and execute the complete integration tests.
This will add all the applicable classes in reflect-config.json
and other native-image
configuration files.
org.springframework.core.codec.CodecException: Type definition error: [simple type, class com.toomuch2learn.reactive.nativebuild.catalogue.crud.model.CatalogueItem];
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of `com.toomuch2learn.reactive.nativebuild.catalogue.crud.model.CatalogueItem` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (io.netty.buffer.ByteBufInputStream); line: 2, column: 2]
at org.springframework.http.codec.json.AbstractJackson2Decoder.processException(AbstractJackson2Decoder.java:211) ~[na:na]
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of `com.toomuch2learn.reactive.nativebuild.catalogue.crud.model.CatalogueItem` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (io.netty.buffer.ByteBufInputStream); line: 2, column: 2]
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1615) ~[na:na]
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[na:na]
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1077) ~[na:na]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1332) ~[na:na]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331) ~[na:na]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164) ~[na:na]
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:2079) ~[na:na]
org.springframework.beans.factory.BeanDefinitionStoreException:
I/O failure during classpath scanning; nested exception is java.io.FileNotFoundException: class path resource [com/toomuch2learn/reactive/nativebuild/catalogue/crud/configuration/FileStorageProperties.class] cannot be opened because it does not exist
at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.addCandidateComponentsFromIndex(ClassPathScanningCandidateComponentProvider.java:410) ~[na:na]
at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:312) ~[na:na]
at org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:276) ~[na:na]
at org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:132) ~[na:na]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:295) ~[na:na]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:249) ~[na:na]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:206) ~[na:na]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:174) ~[na:na]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:336)
~[com.toomuch2learn.reactive.nativebuild.catalogue.crud.springreactivenativecataloguecrudapplication:na]
GraalVM-based native build are more memory & CPU intensive than regular pure Java builds.
Below is htop
stats captured when creating native executable. As observed, all the logical CPUs utilization are at their peaks with memory utilization beyond 70% when no other application running in background during the build.
native-image
consumes a lot of RAM, it is recommanded to use machine with at least 16G of RAM to build native executable to supress Out of memory errors.
We should definetly be stunned by the blazing startup speed achieved for Spring based applications when natively compiled with GraalVM.
Integrating with spring-graalvm-native
project has easily ported a spring application to run in Native Mode. This will retain die hard spring fans to stay and enjoy building Native Images with Spring Framework rather than migrating to Cloud Native frameworks such as Quarkus
, Helidon
or Micronaut
.
Though this article is extension of
And many moreβ¦