GraalVM native image and VaadinSecurityFilterChainBean

I tried to build a native image of my Vaadin Flow app (24.4.0.beta3)

The build works fine but if I start the app I get this exception:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0: Error creating bean with name 'VaadinSecurityFilterChainBean': Instantiation of supplied bean failed
        at org.springframework.beans.factory.aot.AutowiredMethodArgumentsResolver.resolveArguments(AutowiredMethodArgumentsResolver.java:187) ~[na:na]
        at org.springframework.beans.factory.aot.AutowiredMethodArgumentsResolver.resolve(AutowiredMethodArgumentsResolver.java:137) ~[na:na]
        at org.springframework.beans.factory.aot.AutowiredMethodArgumentsResolver.resolve(AutowiredMethodArgumentsResolver.java:123) ~[na:na]
        at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration__Autowiring.apply(WebSecurityConfiguration__Autowiring.java:24) ~[na:na]
        at org.springframework.beans.factory.support.InstanceSupplier$1.get(InstanceSupplier.java:83) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:949) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1217) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1161) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[jtaf4:6.1.6]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[jtaf4:6.1.6]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[jtaf4:6.1.6]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[na:na]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[jtaf4:3.2.5]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[jtaf4:3.2.5]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[jtaf4:3.2.5]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[jtaf4:3.2.5]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[jtaf4:3.2.5]
        at ch.jtaf.Jtaf4Application.main(Jtaf4Application.java:20) ~[jtaf4:na]
        at java.base@22/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH) ~[na:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'VaadinSecurityFilterChainBean': Instantiation of supplied bean failed
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1223) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1161) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1689) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1653) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeanCollection(DefaultListableBeanFactory.java:1543) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1511) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1392) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1353) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.aot.AutowiredMethodArgumentsResolver.resolveArguments(AutowiredMethodArgumentsResolver.java:179) ~[na:na]
        ... 24 common frames omitted
Caused by: java.lang.IllegalStateException: Could not locate field 'rolePrefix' on class class java.lang.Object
        at org.springframework.security.util.FieldUtils.getField(FieldUtils.java:53) ~[na:na]
        at org.springframework.security.util.FieldUtils.getField(FieldUtils.java:51) ~[na:na]
        at org.springframework.security.util.FieldUtils.getField(FieldUtils.java:51) ~[na:na]
        at com.vaadin.flow.spring.security.VaadinRolePrefixHolder.resetRolePrefix(VaadinRolePrefixHolder.java:127) ~[jtaf4:na]
        at com.vaadin.flow.spring.security.VaadinRolePrefixHolder.resetRolePrefix(VaadinRolePrefixHolder.java:114) ~[jtaf4:na]
        at java.base@22/java.util.Optional.ifPresent(Optional.java:178) ~[jtaf4:na]
        at com.vaadin.flow.spring.security.VaadinRolePrefixHolder.resetRolePrefix(VaadinRolePrefixHolder.java:109) ~[jtaf4:na]
        at com.vaadin.flow.spring.security.VaadinWebSecurity.lambda$filterChain$1(VaadinWebSecurity.java:155) ~[jtaf4:na]
        at java.base@22/java.util.Optional.ifPresent(Optional.java:178) ~[jtaf4:na]
        at com.vaadin.flow.spring.security.VaadinWebSecurity.filterChain(VaadinWebSecurity.java:154) ~[jtaf4:na]
        at ch.jtaf.configuration.security.SecurityConfiguration$$SpringCGLIB$$0.CGLIB$filterChain$3(<generated>) ~[jtaf4:na]
        at ch.jtaf.configuration.security.SecurityConfiguration$$SpringCGLIB$$FastClass$$0.invoke(<generated>) ~[jtaf4:na]
        at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) ~[jtaf4:6.1.6]
        at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[na:na]
        at ch.jtaf.configuration.security.SecurityConfiguration$$SpringCGLIB$$0.filterChain(<generated>) ~[jtaf4:na]
        at com.vaadin.flow.spring.security.VaadinWebSecurity__BeanDefinitions.lambda$getVaadinSecurityFilterChainBeanInstanceSupplier$0(VaadinWebSecurity__BeanDefinitions.java:22) ~[na:na]
        at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:68) ~[jtaf4:6.1.6]
        at org.springframework.util.function.ThrowingBiFunction.apply(ThrowingBiFunction.java:54) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.lambda$get$2(BeanInstanceSupplier.java:206) ~[na:na]
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58) ~[jtaf4:6.1.6]
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.invokeBeanSupplier(BeanInstanceSupplier.java:218) ~[na:na]
        at org.springframework.beans.factory.aot.BeanInstanceSupplier.get(BeanInstanceSupplier.java:206) ~[na:na]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.obtainInstanceFromSupplier(DefaultListableBeanFactory.java:949) ~[jtaf4:6.1.6]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1217) ~[jtaf4:6.1.6]
        ... 39 common frames omitted

It seem like we need to add some more runtime hints for the AOT compiler. There’s an ongoing PR for Hilla specific changes, but it seems like there are further changes needed on the platform level.

Can you create a GH issue? :pray:

Sure! I’ll do that right away.

1 Like

This should now be fixed in 24.4.0.beta5. I tested it locally and it worked. Let me know if you run into further issues.

Hi @marcushellberg

the build works but when I start the app I get:

java.lang.NoClassDefFoundError: Could not initialize class javax.imageio.ImageIO
        at com.vaadin.flow.server.PwaRegistry.getBaseImage(PwaRegistry.java:369) ~[na:na]
        at com.vaadin.flow.server.PwaRegistry.initializeResources(PwaRegistry.java:137) ~[na:na]
        at com.vaadin.flow.server.PwaRegistry.<init>(PwaRegistry.java:113) ~[na:na]
        at com.vaadin.flow.server.PwaRegistry.getInstance(PwaRegistry.java:300) ~[na:na]
        at java.base@22/java.util.Optional.map(Optional.java:260) ~[jtaf4:na]
        at com.vaadin.flow.server.VaadinServletService.getPwaRegistry(VaadinServletService.java:233) ~[jtaf4:24.4.0.beta5]
        at com.vaadin.flow.server.VaadinService.lambda$createRequestHandlers$20741bb$1(VaadinService.java:340) ~[jtaf4:24.4.0.beta5]
        at com.vaadin.flow.server.communication.PwaHandler.handleRequest(PwaHandler.java:140) ~[na:na]
        at com.vaadin.flow.server.VaadinService.handleRequest(VaadinService.java:1584) ~[jtaf4:24.4.0.beta5]
        at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:398) ~[jtaf4:24.4.0.beta5]
        at com.vaadin.flow.spring.SpringServlet.service(SpringServlet.java:106) ~[jtaf4:na]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[jtaf4:6.0]

Any idea how to solve that?

Native image and AWT combination seem to be a pain.

We already applied fixes in the past releases, but every GraalVM version brings in some surprise.
For some reason, usage of ImageIO.read in PwaRegistry seems not to be detected at native compilation.

While waiting for a fix in Flow, a workaround that can you can try to apply to your project is to add the following RuntimeHintsRegistrar class and reference it in one of your configuration classes.
Hope that helps you (at least “it works on my machine” :wink:)

public class AdditionalVaadinHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
    	// System.load and System.setProperty seems to be required
        hints.jni()
        .registerType(TypeReference.of("java.lang.System"), MemberCategory.INVOKE_PUBLIC_METHODS);
    }
}

@Configuration
@ImportRuntimeHints(AdditionalVaadinHints.class)
public class MyConfig { }

For the record, here’s a similar config in Quarkus:

Anyway, probably the proper fix for Vaadin PWA support, would be to generate icons during production build.

probably the proper fix for Vaadin PWA support, would be to generate icons during production build.

(Thumbs up)

Additionally: Vote for GraalVM smoke test with basic feature coverage :man_bowing:

3 Likes

We should really move the PWA icon generation to build time. It doesn’t make a lot of sense to keep regenerating static images at runtime.

2 Likes