Search code examples
javaspring-bootmavengraalvm-native-imagespring-native

Spring Native runtime error : Unsupported method java.lang.Class.isSealed() is reachable


I am trying to execute my native image built by Spring Native and Spring Boot. Executing my app generated in the /target directory give me the following error :

WARN 18883 --- [           main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodValidationPostProcessor': Unexpected exception during bean creation; nested exception is com.oracle.svm.core.jdk.UnsupportedFeatureError: Unsupported method java.lang.Class.isSealed() is reachable: The declaring class of this element has been substituted, but this element is not present in the substitution class
ERROR 18883 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodValidationPostProcessor': Unexpected exception during bean creation; nested exception is com.oracle.svm.core.jdk.UnsupportedFeatureError: Unsupported method java.lang.Class.isSealed() is reachable: The declaring class of this element has been substituted, but this element is not present in the substitution class
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:555) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[na:na]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[na:na]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213) ~[na:na]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:258) ~[na:na]
        at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:762) ~[na:na]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:567) ~[na:na]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[na:na]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[clea-ws-rest:2.7.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[clea-ws-rest:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[clea-ws-rest:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[clea-ws-rest:2.7.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[clea-ws-rest:2.7.0]
        at fr.gouv.clea.ws.CleaWsRestApplication.main(CleaWsRestApplication.java:11) ~[clea-ws-rest:0-SNAPSHOT]
Caused by: com.oracle.svm.core.jdk.UnsupportedFeatureError: Unsupported method java.lang.Class.isSealed() is reachable: The declaring class of this element has been substituted, but this element is not present in the substitution class
        at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:87) ~[na:na]
        at java.lang.Class.isSealed(DynamicHub.java:4563) ~[clea-ws-rest:na]
        at java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282) ~[na:na]
        at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:266) ~[na:na]
        at org.springframework.aop.framework.AopProxyUtils.completeProxiedInterfaces(AopProxyUtils.java:146) ~[na:na]
        at org.springframework.aop.framework.JdkDynamicAopProxy.<init>(JdkDynamicAopProxy.java:111) ~[na:na]
        at org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(DefaultAopProxyFactory.java:55) ~[na:na]
        at org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy(ProxyCreatorSupport.java:105) ~[na:na]
        at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:110) ~[na:na]
        at org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver.buildLazyResolutionProxy(ContextAnnotationAutowireCandidateResolver.java:130) ~[na:na]
        at org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver.getLazyResolutionProxyIfNecessary(ContextAnnotationAutowireCandidateResolver.java:54) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1306) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedConstructionResolver.lambda$resolve$0(InjectedConstructionResolver.java:83) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedConstructionResolver.resolveDependency(InjectedConstructionResolver.java:97) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedConstructionResolver.resolve(InjectedConstructionResolver.java:83) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedElementResolver.resolve(InjectedElementResolver.java:35) ~[na:na]
        at org.springframework.aot.beans.factory.InjectedElementResolver.create(InjectedElementResolver.java:66) ~[na:na]
        at org.springframework.aot.beans.factory.BeanDefinitionRegistrar$BeanInstanceContext.create(BeanDefinitionRegistrar.java:211) ~[na:na]
        at org.springframework.aot.ContextBootstrapInitializer.lambda$initialize$39(ContextBootstrapInitializer.java:378) ~[na:na]
        at org.springframework.aot.beans.factory.BeanDefinitionRegistrar$ThrowableFunction.apply(BeanDefinitionRegistrar.java:294) ~[na:na]
        at org.springframework.aot.beans.factory.BeanDefinitionRegistrar.lambda$instanceSupplier$0(BeanDefinitionRegistrar.java:115) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[na:na]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[na:na]

I use the following dependencies in my pom.xml :

       <dependency>
            <groupId>org.springframework.experimental</groupId>
            <artifactId>spring-native</artifactId>
            <version>0.12.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.experimental</groupId>
            <artifactId>spring-aot</artifactId>
            <version>0.12.0</version>
        </dependency>

these plugins :

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.7.0</version>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder:tiny</builder>
                        <env>
                            <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
                        </env>
                    </image>
                    <classifier>exec</classifier>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.experimental</groupId>
                <artifactId>spring-aot-maven-plugin</artifactId>
                <version>0.12.0</version>
            </plugin>
            <plugin>
                <groupId>org.springframework.experimental</groupId>
                <artifactId>spring-aot-maven-plugin</artifactId>
                <version>0.12.0</version>
                <executions>
                    <execution>
                        <id>generate</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

and these profile for building native image :

      <profile>
            <id>native</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <version>0.9.11</version>
                        <executions>
                            <execution>
                                <id>build-native</id>
                                <goals>
                                    <goal>build</goal>
                                </goals>
                                <phase>package</phase>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <classifier>exec</classifier>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>

To build image, i use these commands lines : mvn package -Pnative or mvn package spring-boot:build-image; then, the result appear in the /target directory, i use ./target/myApp to run it.

Here is the result of java --version :

openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment GraalVM CE 21.3.2 (build 17.0.3+7-jvmci-21.3-b14)
OpenJDK 64-Bit Server VM GraalVM CE 21.3.2 (build 17.0.3+7-jvmci-21.3-b14, mixed mode, sharing)

I found this problem on github which seems very similar to mine, if not identical. The ticket has been closed but I don't understand the resolution.

The error seems to have something to do with the profiles related to the environments (dev,prod,test ...). A section of the Spring Native documentation is dedicated to this aspect. To solve the error, I filled in a profile in the application.yml configuration file. The profile is correctly loaded during the run time but the error is the same.

Wouldn't the error come from a compatibility problem between the jdk and the Spring libraries? However, in the javadoc, the method is present since the jdk version 17.

Thanks in advance


Solution

  • From the github issue that you linked we can see that the solution was to add reflection support on sealed classes in GraalVM.

    This was merged to master via the following pull request.

    In the GraalVM release notes we can see that this made it into the releases from version 22.0.0 and higher.

    22.0.0 (2021-01-18)

    This date seems wrong, it's probably 2022-01-18

    Added support for reflective introspection of sealed classes on JDK 17: Class.isSealed() and Class.getPermittedSubclasses().

    Your current JDK is using GraalVM 21.3.2, so that doesn't contain this fix yet. The solution would be to find a JDK build that has version 22.x or higher of GraalVM.