hilla icon indicating copy to clipboard operation
hilla copied to clipboard

fix: add resource pattern for file-routes.json

Open rbrki07 opened this issue 9 months ago • 5 comments

Description

This should fix that file-routes.jsoncannot be loaded from /META-INF/VAADIN/ when Hilla application is executed as native image.

Fixes #2395

Type of change

  • [X] Bugfix
  • [ ] Feature

Checklist

  • [X] I have read the contribution guide: https://vaadin.com/docs/latest/guide/contributing/overview/
  • [X] I have added a description following the guideline.
  • [X] The issue is created in the corresponding repository and I have referenced it.
  • [ ] I have added tests to ensure my change is effective and works as intended.
  • [X] New and existing tests are passing locally with my change.
  • [X] I have performed self-review and corrected misspellings.

rbrki07 avatar May 07 '24 14:05 rbrki07

I followed your recommendation @taefi and made the suggested change. Unfortunately, my knowledge about the Hilla project-, build- and test-setup is not sufficient, which means, that I don't know how to verify that the change fixes the issue and I don't know how to add a test case for this particular fix.

rbrki07 avatar May 07 '24 14:05 rbrki07

I followed your recommendation @taefi and made the suggested change. Unfortunately, my knowledge about the Hilla project-, build- and test-setup is not sufficient, which means, that I don't know how to verify that the change fixes the issue and I don't know how to add a test case for this particular fix.

@rbrki07 while we're looking at this issue, if you're interested in testing your changes locally, it should not be complicated:

  1. checkout the main branch Hilla repo, and make your changes locally (you've already done)
  2. build the repo locally using: mvn clean install -DskipTests (from project root, or the changed module(s))
  3. override the 24.5-SNAPSHOT (version of the main branch) of the changed module(s) (endpoint in this case) in pom.xml of your playground project.
  4. build for native image and run to see if that solved the issue.

For overriding the dependency of the changed module in your playground project, use the latest publicly available version of the Hilla/Vaadin (at this time 24.4.0.beta1) and put the dependency of the change module(s) on top of the vaadin dependency (order is important):

<project>
    <properties>
        <java.version>17</java.version>
        <vaadin.version>24.4.0.beta1</vaadin.version>
    </properties>
    
    <!-- skipped listing the repositories and pluginRepositories as you already have the prereleases -->

    <dependencies>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>hilla-endpoint</artifactId>
            <version>24.5-SNAPSHOT</version>
        </dependency>
       <!-- make sure the dependency of the changed module(s) listed on top of the vaadin managed dependencies -->
       <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-spring-boot-starter</artifactId>
        </dependency>
       <!-- rest of the dependencies -->
    <dependencies/>

</project>

Maven uses the timestamp of the latest snapshot (either built locally or coming from our regular builds). So, make sure to build Hilla locally right before testing it in your playground project to avoid confusions about which snapshot is in use. Hope this helps.

taefi avatar May 10 '24 07:05 taefi

Thank you @taefi. I will try your instructions and check it locally. I will also apply your suggested changes. I will come back to this pr in a few days, because I'm AFK for a few days.

rbrki07 avatar May 10 '24 09:05 rbrki07

I followed your instruction @taefi, and I was able to test the change locally 🥳 I tried different variations, but unfortunately none of them worked as desired yet. I will come back to this soon.

rbrki07 avatar May 13 '24 19:05 rbrki07

CLA assistant check
All committers have signed the CLA.

CLAassistant avatar May 16 '24 18:05 CLAassistant

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 95.14%. Comparing base (9be358f) to head (a380a4a).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2396   +/-   ##
=======================================
  Coverage   95.14%   95.14%           
=======================================
  Files          66       66           
  Lines        4512     4512           
  Branches      650      650           
=======================================
  Hits         4293     4293           
  Misses        178      178           
  Partials       41       41           
Flag Coverage Δ
unittests 95.14% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov[bot] avatar May 16 '24 18:05 codecov[bot]

Thanks for your contribution @marcushellberg!

To test your change, I created a new project using npx @hilla/cli@latest init --next native-image-test, which uses Vaadin 24.4.0.beta3. I also applied your change as a patch for HillaHintsRegistrar.java, like you did in your project travel-tips: https://github.com/marcushellberg/travel-tips/blob/main/src/main/java/com/vaadin/hilla/springnative/HillaHintsRegistrar.java. grafik

Compiling the app into a native image using mvn clean package -Pproduction -Pnative native:compile works fine. When I start the app as native image using ./target/native-image-test, I'm getting an error and the following stack trace:

2024-05-16T22:40:51.950+02:00 ERROR 9183 --- [           main] c.v.hilla.route.ClientRouteRegistry      : Failed to load file-routes.json from /META-INF/VAADIN/file-routes.json

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.vaadin.hilla.route.records.ClientViewMenuConfig`: cannot deserialize from Object value (no delegate- or property-based Creator): this appears to be a native image, in which case you may need to configure reflection for the class that is to be deserialized
 at [Source: (ByteArrayInputStream); line: 1, column: 80] (through reference chain: java.util.ArrayList[0]->com.vaadin.hilla.route.records.ClientViewConfig["children"]->java.util.ArrayList[0]->com.vaadin.hilla.route.records.ClientViewConfig["menu"])
	at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1915) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:414) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1355) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1431) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:352) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) ~[na:na]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:359) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129) ~[na:na]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:314) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:177) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:359) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4825) ~[native-image-test:2.15.4]
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3817) ~[native-image-test:2.15.4]
	at com.vaadin.hilla.route.ClientRouteRegistry.registerClientRoutes(ClientRouteRegistry.java:162) ~[native-image-test:na]
	at com.vaadin.hilla.startup.RouteUnifyingServiceInitListener.serviceInit(RouteUnifyingServiceInitListener.java:101) ~[native-image-test:na]
	at com.vaadin.flow.server.VaadinService.lambda$init$0(VaadinService.java:236) ~[native-image-test:24.4.0.beta3]
	at [email protected]/java.util.Iterator.forEachRemaining(Iterator.java:133) ~[native-image-test:na]
	at [email protected]/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939) ~[na:na]
	at [email protected]/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) ~[native-image-test:na]
	at [email protected]/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) ~[native-image-test:na]
	at [email protected]/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) ~[na:na]
	at com.vaadin.flow.server.VaadinService.lambda$init$1(VaadinService.java:236) ~[native-image-test:24.4.0.beta3]
	at com.vaadin.flow.server.VaadinService.runWithServiceContext(VaadinService.java:2372) ~[native-image-test:24.4.0.beta3]
	at com.vaadin.flow.server.VaadinService.init(VaadinService.java:234) ~[native-image-test:24.4.0.beta3]
	at com.vaadin.flow.spring.SpringVaadinServletService.init(SpringVaadinServletService.java:102) ~[na:na]
	at com.vaadin.flow.spring.SpringServlet.createServletService(SpringServlet.java:115) ~[native-image-test:na]
	at com.vaadin.flow.server.VaadinServlet.createServletService(VaadinServlet.java:336) ~[native-image-test:24.4.0.beta3]
	at com.vaadin.flow.server.VaadinServlet.init(VaadinServlet.java:132) ~[native-image-test:24.4.0.beta3]
	at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:944) ~[na:na]
	at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:808) ~[na:na]
	at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedContext.load(TomcatEmbeddedContext.java:84) ~[native-image-test:3.2.5]
	at [email protected]/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
	at [email protected]/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) ~[na:na]
	at [email protected]/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) ~[na:na]
	at [email protected]/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) ~[na:na]
	at [email protected]/java.util.TreeMap$ValueSpliterator.forEachRemaining(TreeMap.java:3250) ~[na:na]
	at [email protected]/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[native-image-test:na]
	at [email protected]/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[native-image-test:na]
	at [email protected]/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[native-image-test:na]
	at [email protected]/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
	at [email protected]/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[native-image-test:na]
	at [email protected]/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[native-image-test:na]
	at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedContext.lambda$deferredLoadOnStartup$0(TomcatEmbeddedContext.java:67) ~[native-image-test:3.2.5]
	at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedContext.doWithThreadContextClassLoader(TomcatEmbeddedContext.java:108) ~[native-image-test:3.2.5]
	at org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedContext.deferredLoadOnStartup(TomcatEmbeddedContext.java:66) ~[native-image-test:3.2.5]
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.performDeferredLoadOnStartup(TomcatWebServer.java:329) ~[native-image-test:3.2.5]
	at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:237) ~[native-image-test:3.2.5]
	at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:44) ~[na:na]
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:288) ~[native-image-test:6.1.6]
	at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:471) ~[native-image-test:6.1.6]
	at [email protected]/java.lang.Iterable.forEach(Iterable.java:75) ~[native-image-test:na]
	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:260) ~[native-image-test:6.1.6]
	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:205) ~[native-image-test:6.1.6]
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:981) ~[native-image-test:6.1.6]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) ~[native-image-test:6.1.6]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[native-image-test:3.2.5]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[native-image-test:3.2.5]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[native-image-test:3.2.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[native-image-test:3.2.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[native-image-test:3.2.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[native-image-test:3.2.5]
	at com.example.application.Application.main(Application.java:20) ~[native-image-test:na]
	at [email protected]/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH) ~[na:na]

Can you reproduce this?

rbrki07 avatar May 16 '24 20:05 rbrki07

Adding a runtime hint for ClientViewMenuConfig.class makes it possible to start a starter created with hilla init --pre. But there are no menu items visible, and trying to call the HelloWorldEndpoint results in:

The program tried to reflectively invoke method

  public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) without it being registered for runtime reflection. 
Add public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) to the reflection metadata to solve this problem.

marcushellberg avatar May 17 '24 06:05 marcushellberg

Adding this to registerHints in HillaHintsRegistrar.java

        hints.resources().registerPattern("file-routes.json");
        hints.reflection().registerType(ClientViewConfig.class, MemberCategory.values());
        hints.reflection().registerType(ClientViewMenuConfig.class, MemberCategory.values());
        hints.reflection().registerType(AvailableViewInfo.class, MemberCategory.values());

results in a startup of a native compiled Hilla app without errors and I even see the menu items 😍

./target/native-image-test                            
              _   _                 _                                  _            _   
  _ __   __ _| |_(_)_   _____      (_)_ __ ___   __ _  __ _  ___      | |_ ___  ___| |_ 
 | '_ \ / _` | __| \ \ / / _ \_____| | '_ ` _ \ / _` |/ _` |/ _ \_____| __/ _ \/ __| __|
 | | | | (_| | |_| |\ V /  __/_____| | | | | | | (_| | (_| |  __/_____| ||  __/\__ \ |_ 
 |_| |_|\__,_|\__|_| \_/ \___|     |_|_| |_| |_|\__,_|\__, |\___|      \__\___||___/\__|
                                                      |___/                             

2024-05-17T10:15:42.475+02:00  INFO 30849 --- [           main] com.example.application.Application      : Starting AOT-processed Application using Java 21.0.2 with PID 30849 (/Users/rwilby/Documents/workspaces/hilla/native-image-test/target/native-image-test started by rwilby in /Users/rwilby/Documents/workspaces/hilla/native-image-test)
2024-05-17T10:15:42.475+02:00  INFO 30849 --- [           main] com.example.application.Application      : No active profile set, falling back to 1 default profile: "default"
2024-05-17T10:15:42.499+02:00  INFO 30849 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-05-17T10:15:42.500+02:00  INFO 30849 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-05-17T10:15:42.500+02:00  INFO 30849 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.20]
2024-05-17T10:15:42.511+02:00  INFO 30849 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-05-17T10:15:42.511+02:00  INFO 30849 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 36 ms
2024-05-17T10:15:42.531+02:00  INFO 30849 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing AtmosphereFramework
2024-05-17T10:15:42.608+02:00  WARN 30849 --- [           main] o.a.cpr.DefaultAnnotationProcessor       : Unable to detect annotations. Application may fail to deploy.
2024-05-17T10:15:42.609+02:00  INFO 30849 --- [           main] c.v.f.s.DefaultDeploymentConfiguration   : Vaadin is running in production mode.
2024-05-17T10:15:42.610+02:00  INFO 30849 --- [           main] c.vaadin.flow.spring.SpringInstantiator  : The number of beans implementing 'I18NProvider' is 0. Cannot use Spring beans for I18N, falling back to the default behavior
2024-05-17T10:15:42.614+02:00  INFO 30849 --- [           main] com.vaadin.flow.server.Platform          : Unable to determine Hilla version. No META-INF/maven/com.vaadin/hilla/pom.properties found
2024-05-17T10:15:42.616+02:00  INFO 30849 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path ''
2024-05-17T10:15:42.616+02:00  INFO 30849 --- [           main] com.example.application.Application      : Started Application in 0.167 seconds (process running for 0.183)
2024-05-17T10:15:47.513+02:00  INFO 30849 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-05-17T10:15:47.513+02:00  INFO 30849 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-05-17T10:15:47.514+02:00  INFO 30849 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms

grafik

Invoking com.example.application.services.HelloWorldService.sayHello by using the Say hello results in the following stack trace:

2024-05-17T10:19:31.135+02:00 ERROR 30849 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) without it being registered for runtime reflection. Add public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.] with root cause

org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) without it being registered for runtime reflection. Add public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.forQueriedOnlyExecutable(MissingReflectionRegistrationUtils.java:72) ~[na:na]
	at [email protected]/java.lang.reflect.Method.acquireMethodAccessor(Method.java:77) ~[native-image-test:na]
	at [email protected]/java.lang.reflect.Method.invoke(Method.java:577) ~[native-image-test:na]
	at com.vaadin.hilla.EndpointInvoker.invokeVaadinEndpointMethod(EndpointInvoker.java:439) ~[native-image-test:na]
	at com.vaadin.hilla.EndpointInvoker.invoke(EndpointInvoker.java:207) ~[native-image-test:na]
	at com.vaadin.hilla.EndpointController.serveEndpoint(EndpointController.java:199) ~[native-image-test:na]
	at [email protected]/java.lang.reflect.Method.invoke(Method.java:580) ~[native-image-test:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) ~[native-image-test:6.1.6]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[native-image-test:6.1.6]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[native-image-test:6.1.6]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[native-image-test:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[native-image-test:6.1.6]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[native-image-test:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:206) ~[na:na]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[native-image-test:10.1.20]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[native-image-test:6.1.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[native-image-test:6.1.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[native-image-test:6.1.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[native-image-test:6.1.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[native-image-test:6.1.6]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[native-image-test:6.1.6]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[na:na]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[na:na]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[native-image-test:10.1.20]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[na:na]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[native-image-test:10.1.20]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[na:na]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[na:na]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[na:na]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[native-image-test:10.1.20]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[na:na]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736) ~[na:na]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[native-image-test:10.1.20]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[na:na]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[na:na]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[na:na]
	at [email protected]/java.lang.Thread.runWith(Thread.java:1596) ~[native-image-test:na]
	at [email protected]/java.lang.Thread.run(Thread.java:1583) ~[native-image-test:na]
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:833) ~[native-image-test:na]
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211) ~[na:na]

rbrki07 avatar May 17 '24 08:05 rbrki07

Progress!

I wonder what has changed in calling endpoints. It used to work. Have we changed how endpoints are being called?

marcushellberg avatar May 17 '24 08:05 marcushellberg

I just realized that AvailableViewInfo and ClientViewMenuConfig no longer exist due to https://github.com/vaadin/hilla/pull/2425 😞. I think, I have to give it another try with beta4.

rbrki07 avatar May 17 '24 08:05 rbrki07

The AvailableViewInfo from Hilla is replaced with com.vaadin.flow.server.menu.AvailableViewInfo so we probably need to register that instead. Also, instead of ClientViewMenuConfig we should register com.vaadin.flow.router.MenuData.

taefi avatar May 17 '24 09:05 taefi

Have we changed how endpoints are being called?

I don't recall any change in that zone. The stacktrace points to this method invocation that uses the reflection API, but the code hasn't changed since early 2022.

taefi avatar May 17 '24 09:05 taefi

This is the code that is supposed to register all needed types for endpoints: https://github.com/vaadin/hilla/blob/8f142a3f43662405160b920710f7bef5a5290004/packages/java/endpoint/src/main/java/com/vaadin/hilla/springnative/HillaHintsRegistrar.java#L56-L79

Artur- avatar May 17 '24 11:05 Artur-

Well, looking at the above code, it tries to read the open api json from /hilla-openapi.json which means the root of target/classes, and while reading it from this location works for registerEndpoints at a normal PROD mode, it seems that if fails during the native build (the following is from my local build of the native IT project):

Screenshot 2024-05-17 at 15 58 49

Then, obviously it fails to call the endpoint's method at runtime since no methods has been registered during the build.

What is weird is:

  • When I look at the target/classes the hilla-openapi.json is there, so why it fails to load it?
  • How this was working before? I know that hilla-openapi.json was at classes/com/vaadin/hilla/openapi.json, so previously it was being loaded from /com/vaadin/hilla/openapi.json, and recently we moved it to the root of target/classes and renamed it hilla-openapi.json. How is it different for the resource loader? Is - in the name of the file the culprit? If so, how is fine in EndpointRegisteryInitializer?

taefi avatar May 17 '24 13:05 taefi

It seems that the - in the file name of hilla-openapi.json was somehow making it inaccessible to the class/resource loader at the time of native build, even though, it is working fine during a normal PROD runtime. I changed it locally to hillaopenapi.json and this time I didn't get that error log during the build.

taefi avatar May 17 '24 13:05 taefi

Nope :( It is happening again (though I had a successful native build earlier):

Screenshot 2024-05-17 at 16 54 27

I suspected that timing might be the issue, for instance when that the open api json is not yet available during the native build, but it created during the production build much earlier than the native build is getting started.

taefi avatar May 17 '24 13:05 taefi

I've noticed a weird pattern of having "Resource /hilla-openapi.json is not available" during the native build: If I change the resource name e.g. from hilla-openapi.json to hillaopenapi.json and vice versa (in the Hilla repo), next time I clean & build hilla/packages/java/tests/spring/native, I don't get the above mentioned error log, but any consequent clean & build would get the same error, but surprizingly, the endpoints are working in both cases. Probably, that registerEndpointTypes method is being called more than once (for some reason that I don't no yet, need to debug) and the second time that resource is available and it can register the endpoint types. Easiest way of debugging would be to add an info log for the times that it finds the resource, then the users can also see what's happening during the build.

taefi avatar May 20 '24 07:05 taefi

Well, as suspected, adding a log shows when registerEndpointTypes is called on the second round of spring-boot startup (during the native build) the /hilla-openapi.json resource is available and that's why the endpoints are working fine in hilla/packages/java/tests/spring/native:

Screenshot 2024-05-20 at 11 31 01

Still, it could be investigated that why a change of name in hilla-openapi.json file makes it available in the first run as well (so no "Resource /hilla-openapi.json is not available" is flushed out), but it is not a priority at the moment.

taefi avatar May 20 '24 08:05 taefi

I pushed the changes needed for a Vaadin/Hilla 24.4 applications to work with the dynamic menu. If someone wants to run the native build in hilla/packages/java/tests/spring/native, they need to add <enforcer.skip>true</enforcer.skip> to the properties since there are internal convergence issues with the testbech ATM. For applications with security enabled, there is another runtime issue that should be addressed in Flow.

taefi avatar May 20 '24 13:05 taefi

Hi @taefi, is there already an issue where you try to fix the problem when calling endpoints, as discussed here: https://github.com/vaadin/hilla/pull/2396#issuecomment-2117188338? Using beta4 I still get this error, when I try to invoke HelloWorldService.sayHello:

2024-05-23T13:41:31.953+02:00 ERROR 56424 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) without it being registered for runtime reflection. Add public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.] with root cause

org.graalvm.nativeimage.MissingReflectionRegistrationError: The program tried to reflectively invoke method public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) without it being registered for runtime reflection. Add public java.lang.String com.example.application.services.HelloWorldService.sayHello(java.lang.String) to the reflection metadata to solve this problem. See https://www.graalvm.org/latest/reference-manual/native-image/metadata/#reflection for help.
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.forQueriedOnlyExecutable(MissingReflectionRegistrationUtils.java:72) ~[na:na]
        at [email protected]/java.lang.reflect.Method.acquireMethodAccessor(Method.java:77) ~[native-image-test:na]
        at [email protected]/java.lang.reflect.Method.invoke(Method.java:577) ~[native-image-test:na]
        at com.vaadin.hilla.EndpointInvoker.invokeVaadinEndpointMethod(EndpointInvoker.java:440) ~[native-image-test:na]
        at com.vaadin.hilla.EndpointInvoker.invoke(EndpointInvoker.java:207) ~[native-image-test:na]
        at com.vaadin.hilla.EndpointController.serveEndpoint(EndpointController.java:194) ~[native-image-test:na]
        at [email protected]/java.lang.reflect.Method.invoke(Method.java:580) ~[native-image-test:na]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) ~[native-image-test:6.1.6]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[native-image-test:6.1.6]
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[native-image-test:6.1.6]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[native-image-test:6.0]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[native-image-test:6.1.6]
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[native-image-test:6.0]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:206) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[native-image-test:10.1.20]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[native-image-test:6.1.6]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[native-image-test:6.1.6]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[native-image-test:6.1.6]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[native-image-test:6.1.6]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[native-image-test:6.1.6]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[native-image-test:6.1.6]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:175) ~[na:na]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:150) ~[na:na]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[na:na]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[na:na]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[native-image-test:10.1.20]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[na:na]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[native-image-test:10.1.20]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[na:na]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344) ~[na:na]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[na:na]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[native-image-test:10.1.20]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[na:na]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736) ~[na:na]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[native-image-test:10.1.20]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[na:na]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[na:na]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63) ~[na:na]
        at [email protected]/java.lang.Thread.runWith(Thread.java:1596) ~[native-image-test:na]
        at [email protected]/java.lang.Thread.run(Thread.java:1583) ~[native-image-test:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:833) ~[native-image-test:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211) ~[na:na]

rbrki07 avatar May 23 '24 11:05 rbrki07

Ah. Good that you reminded of that. It's not fixed, but needs to be fixed https://github.com/vaadin/hilla/issues/2461

marcushellberg avatar May 23 '24 15:05 marcushellberg

This ticket/PR has been released with Hilla 24.5.0.alpha1 and is also targeting the upcoming stable 24.5.0 version.

vaadin-bot avatar May 31 '24 09:05 vaadin-bot