0

with Spring Boot 3 release there is lots of advertisement about GraalVM and native-images, so I decided experiment little bit..

With Spring Boot Initializer I quickly created a really simple gradle project (one single rest service that is doing nothing special). With the help of the gradle 'native-image' and JiB plugin, I could quickly get a running state with Spring Boot Application as a normal Java Applicaton and native-image Application (and seen the drastic startup time changes).

While JiB created a Docker Image for me, I tried to deploy the SB3 application to my minikube, there I got the first surprise, while I was working with MacOS M1, the image that I created (based on alpine) was complaining the wrong 'exec format' after little bit reading, I learned while my local GraalVM is MacOS based aarch64 it would not run in alpine and I have to create cross platform image and I have to create Docker images based on destination platform GraalVM (in this case linux/alpine) which I did, I bypassed the original problem and Spring Boot 3 application starts but encounters following problem in the stacktrace.

16:43:45.808 [main] ERROR org.springframework.boot.SpringApplication - Application 
run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with 
name 'resourceHandlerMapping': Instantiation of supplied bean failed
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstan ceFromSupplier(AbstractAutowireCapableBeanFactory.java:1236)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157)


Caused by: java.lang.IllegalStateException: No ServletContext set
at org.springframework.util.Assert.state(Assert.java:76)
at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping(WebMvcConfigurationSupport.java:581)
at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport__BeanDefinitions.lambda$getResourceHandlerMappingInstanceSupplier$7(WebMvcConfigurationSupport__BeanDefinitions.java:166)
at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68)
at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54)

Now I tried to look as much as possible what 'native-image' plugin does (while native-image and Spring Boot 3 application runs on my Mac Book), I can't figure out what is the Delta. I think it has to do with 'reflection-config'. 'native-image' plugin created a 'reflect-config.json' and it contains the following lines.

   {
    "name": "resourceHandlerMapping",
    "parameterTypes": [
      "org.springframework.web.accept.ContentNegotiationManager",
      "org.springframework.format.support.FormattingConversionService",
      "org.springframework.web.servlet.resource.ResourceUrlProvider"
    ]
  }

I think should be relevant for the problem, but this file is in the classpath and it is not changing the outcome(I can also see that 'WebMvcConfigurationSupport__BeanDefinitions.java' is also generated and is in the classpath).

One thing I have to say, while the shear number of dependencies Spring Boot needs, I didn't place the whole dependencies in the classpath but I created a fat jar (shadowJar plugin), which includes all my code and dependencies, may be this causing the problem.

'native-image' in my cross platform docker image, is able to create the application with this fat jar but it throws the above mentioned exception.

Anyway, can anybody give any tips to solve this problem.

Or how can diagnose such a problem in GraalVM and native-image?

posthumecaver
  • 1,584
  • 4
  • 16
  • 29
  • You shouldn't need to do anything. I don't see why you would need the fatjar plugin. If you run the build in the container it should generate a binary for the OS version used in the container (you cannot build a different binary on MacOs for linux for instance). The generated java code shouldn't be different on Mac, Linux or windows only the resulting binary code will be different. – M. Deinum Dec 14 '22 at 09:03
  • 'native-image' plugin generates different binary code. Off course if I run the application pure Java application, you are absolutely right, created classes should run in any platform but GraalVM and 'native-image' plugin are generating binaries for the OS they are runnning (my MacOS M1 GraalVM generates binaries for MacOS M1). For that reason I created another Docker Image that based a Linux GraalVM and try to generate binary for Linux, which partially succeeded and I pass the fat jar to Docker Image while I don't want to COPY every dependencies one by one to Docker Image. – posthumecaver Dec 14 '22 at 09:21
  • You shouldn't copy the jar, what you should do is make a build that runs on docker, use a docker multistage build for that. Do everything in docker to create the image. If you do it partially on a mac things might get left out. Regarding the fatjar if you really copy that (afaik you should copy the code not the jar) you should use the one created by Spring BOot **not one created by some other plugin**. – M. Deinum Dec 14 '22 at 09:44
  • Have you solved your issue? – Patryk Imosa Jun 02 '23 at 10:22
  • Without Google JiB plugin I could not manage to run this because of Reflection configurations, but fortunately GraalVM now has Reflection Meta Directory, https://github.com/oracle/graalvm-reachability-metadata and Google JiB is seamlessly integrated with it, so cross platform builds are no problem, as I explain here https://mehmetsalgar.wordpress.com/2023/02/26/azure-devops-pipelines-for-an-ideal-ci-cd-for-your-kubertnetes-deployments/#graalvm-native-image-building – posthumecaver Jun 06 '23 at 06:47

0 Answers0