Home > Net >  spring-boot 3.0.0 GraaVM for interface jakarta.servlet.http.HttpServletRequest
spring-boot 3.0.0 GraaVM for interface jakarta.servlet.http.HttpServletRequest

Time:12-21

Just testing some Spring Boot 3.0.0 with GraalVM Native and got some queries regarding it as I could not find properly documentation regarding it.

So, I've started a new project with GraalVM Native Support and Spring Web depedencies on Spring Initializr (https://start.spring.io/).

Then, for testing native image purposes I have my DemoApplication class like as follows:

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import jakarta.servlet.http.HttpServletRequest;

@SpringBootApplication(proxyBeanMethods = false)
public class DemoApplication {

    @Autowired
    private HttpServletRequest request;

    public static void main(String[] args) {
       SpringApplication.run(DemoApplication.class, args);
    }   

}

As a result to build a native image, have used command as follows:

mvn -Pnative spring-boot:build-image

The image was successfully compiled and created:

docker images
REPOSITORY                 TAG              IMAGE ID       CREATED        SIZE
paketobuildpacks/run       tiny-cnb         c71fb787280a   3 days ago     17.3MB
paketobuildpacks/builder   tiny             cf7ea4946a20   42 years ago   588MB
demo                       0.0.1-SNAPSHOT   7794949d07ce   42 years ago   96.9MB

When I run this "demo" image using:

docker run demo:0.0.1-SNAPSHOT

It shows the following exception:

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

2022-12-16T21:23:41.386Z  INFO 1 --- [           main] com.example.demo.DemoApplication         : Starting AOT-processed DemoApplication using Java 17.0.5 with PID 1 (/workspace/com.example.demo.DemoApplication started by cnb in /workspace)
2022-12-16T21:23:41.386Z  INFO 1 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to 1 default profile: "default"
2022-12-16T21:23:41.395Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-12-16T21:23:41.396Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-12-16T21:23:41.396Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.1]
2022-12-16T21:23:41.399Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-12-16T21:23:41.400Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 14 ms
2022-12-16T21:23:41.403Z  WARN 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoApplication': Instantiation of supplied bean failed
2022-12-16T21:23:41.403Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2022-12-16T21:23:41.404Z ERROR 1 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'demoApplication': Instantiation of supplied bean failed
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1236) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1210) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1157) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:561) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:961) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:915) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[com.example.demo.DemoApplication:3.0.0]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[com.example.demo.DemoApplication:3.0.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[com.example.demo.DemoApplication:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[com.example.demo.DemoApplication:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[com.example.demo.DemoApplication:3.0.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[com.example.demo.DemoApplication:3.0.0]
        at com.example.demo.DemoApplication.main(DemoApplication.java:16) ~[com.example.demo.DemoApplication:na]
Caused by: com.oracle.svm.core.jdk.UnsupportedFeatureError: Proxy class defined by interfaces [interface jakarta.servlet.http.HttpServletRequest] not found. Generating proxy classes at runtime is not supported. Proxy classes need to be defined at image build time by specifying the list of interfaces that they implement. To define proxy classes use -H:DynamicProxyConfigurationFiles=<comma-separated-config-files> and -H:DynamicProxyConfigurationResources=<comma-separated-config-resources> options.
        at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:89) ~[na:na]
        at com.oracle.svm.core.reflect.proxy.DynamicProxySupport.getProxyClass(DynamicProxySupport.java:171) ~[na:na]
        at [email protected]/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:47) ~[com.example.demo.DemoApplication:na]
        at [email protected]/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:1037) ~[com.example.demo.DemoApplication:na]
        at org.springframework.beans.factory.support.AutowireUtils.resolveAutowiringValue(AutowireUtils.java:134) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1576) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1368) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1325) ~[com.example.demo.DemoApplication:6.0.2]
        at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveValue(AutowiredFieldValueResolver.java:189) ~[na:na]
        at org.springframework.beans.factory.aot.AutowiredFieldValueResolver.resolveAndSet(AutowiredFieldValueResolver.java:167) ~[na:na]
        at com.example.demo.DemoApplication__Autowiring.apply(DemoApplication__Autowiring.java:14) ~[na:na]
        at org.springframework.beans.factory.support.InstanceSupplier$1.get(InstanceSupplier.java:82) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainInstanceFromSupplier(AbstractAutowireCapableBeanFactory.java:1225) ~[com.example.demo.DemoApplication:6.0.2]
        ... 18 common frames omitted

I assume that must be something related to inform a implementation for interface jakarta.servlet.http.HttpServletRequest, however I don't how to inform/configure it.

Do you guys have any suggestion(s)?

Thanks in advance.

CodePudding user response:

After several readings on Spring Boot 3.0.0 (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) user guide, have figured out that some classes Spring AOT needs a runtime hint as a result to compile it into a native program. If the class is not found you need to create a custom hint (https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#native-image.advanced.custom-hints).

So to have it worked I have created a HTTPServletRequest runtime hint (bear in mind that must implements interface org.springframework.aot.hint.RuntimeHintsRegistrar) class as follows:

public class HttpServletRequestRuntimeHint implements RuntimeHintsRegistrar{

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
    try {
        ProxyHints proxies = hints.proxies();
        proxies.registerJdkProxy(HttpServletRequest.class);
    } catch (Exception e) {
        throw new RuntimeException("Could not register RuntimeHint: "   e.getMessage());
    }
    }

}

And then it needs to be informed on a @Configuration class or, for example, your @SpringBootApplication annotated application class to activate those hints:

@SpringBootApplication
@ImportRuntimeHints(value = {HttpServletRequestRuntimeHint.class})
public class DemoApplication {       
    
    @Autowired   
    private HttpServletRequest request;

    public static void main(String[] args) {
        SpringApplication.run(ApplicationConfig.class, args);
    }

}

And done!

Hope that helps!

Thank you.

  • Related