Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add resource pattern for file-routes.json #2396

Merged

Conversation

rbrki07
Copy link
Contributor

@rbrki07 rbrki07 commented May 7, 2024

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

  • Bugfix
  • Feature

Checklist

  • I have read the contribution guide: https://vaadin.com/docs/latest/guide/contributing/overview/
  • I have added a description following the guideline.
  • 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.
  • New and existing tests are passing locally with my change.
  • I have performed self-review and corrected misspellings.

@rbrki07
Copy link
Contributor Author

rbrki07 commented May 7, 2024

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.

@taefi
Copy link
Contributor

taefi commented May 10, 2024

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.

@rbrki07
Copy link
Contributor Author

rbrki07 commented May 10, 2024

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
Copy link
Contributor Author

rbrki07 commented May 13, 2024

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.

@CLAassistant
Copy link

CLAassistant commented May 16, 2024

CLA assistant check
All committers have signed the CLA.

Copy link

codecov bot commented May 16, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

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.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rbrki07
Copy link
Contributor Author

rbrki07 commented May 16, 2024

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 java.base@21.0.2/java.util.Iterator.forEachRemaining(Iterator.java:133) ~[native-image-test:na]
	at java.base@21.0.2/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939) ~[na:na]
	at java.base@21.0.2/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) ~[native-image-test:na]
	at java.base@21.0.2/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735) ~[native-image-test:na]
	at java.base@21.0.2/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 java.base@21.0.2/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
	at java.base@21.0.2/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708) ~[na:na]
	at java.base@21.0.2/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) ~[na:na]
	at java.base@21.0.2/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) ~[na:na]
	at java.base@21.0.2/java.util.TreeMap$ValueSpliterator.forEachRemaining(TreeMap.java:3250) ~[na:na]
	at java.base@21.0.2/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[native-image-test:na]
	at java.base@21.0.2/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[native-image-test:na]
	at java.base@21.0.2/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[native-image-test:na]
	at java.base@21.0.2/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
	at java.base@21.0.2/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[native-image-test:na]
	at java.base@21.0.2/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 java.base@21.0.2/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 java.base@21.0.2/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH) ~[na:na]

Can you reproduce this?

@marcushellberg
Copy link
Member

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.

@rbrki07
Copy link
Contributor Author

rbrki07 commented May 17, 2024

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 java.base@21.0.2/java.lang.reflect.Method.acquireMethodAccessor(Method.java:77) ~[native-image-test:na]
	at java.base@21.0.2/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 java.base@21.0.2/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 java.base@21.0.2/java.lang.Thread.runWith(Thread.java:1596) ~[native-image-test:na]
	at java.base@21.0.2/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]

@marcushellberg
Copy link
Member

Progress!

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

@rbrki07
Copy link
Contributor Author

rbrki07 commented May 17, 2024

I just realized that AvailableViewInfo and ClientViewMenuConfig no longer exist due to #2425 😞. I think, I have to give it another try with beta4.

@taefi
Copy link
Contributor

taefi commented May 17, 2024

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
Copy link
Contributor

taefi commented May 17, 2024

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.

@Artur-
Copy link
Member

Artur- commented May 17, 2024

This is the code that is supposed to register all needed types for endpoints:

private void registerEndpointTypes(RuntimeHints hints) {
try {
var resource = getClass().getResource(openApiResourceName);
if (resource == null) {
logger.error("Resource {} is not available",
openApiResourceName);
return;
}
var reader = new BufferedReader(
new InputStreamReader(resource.openStream()));
String openApiAsText = reader.lines()
.collect(Collectors.joining("\n"));
Set<String> types = OpenAPIUtil.findOpenApiClasses(openApiAsText);
for (String type : types) {
hints.reflection().registerType(TypeReference.of(type),
MemberCategory.values());
}
} catch (IOException e) {
logger.error("Error while scanning and registering endpoint types",
e);
}
hints.resources().registerPattern(EngineConfiguration.OPEN_API_PATH);
}

@taefi
Copy link
Contributor

taefi commented May 17, 2024

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
Copy link
Contributor

taefi commented May 17, 2024

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
Copy link
Contributor

taefi commented May 17, 2024

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
Copy link
Contributor

taefi commented May 20, 2024

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
Copy link
Contributor

taefi commented May 20, 2024

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.

Copy link

sonarcloud bot commented May 20, 2024

Quality Gate Passed Quality Gate passed

Issues
0 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarCloud

@taefi
Copy link
Contributor

taefi commented May 20, 2024

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.

@platosha platosha dismissed taefi’s stale review May 20, 2024 14:00

addressed by the reviewer

@platosha platosha merged commit 6aba671 into vaadin:main May 20, 2024
15 checks passed
vaadin-bot pushed a commit that referenced this pull request May 20, 2024
* fix: add resource pattern for file-routes.json

* apply suggestions from taefi

* Register hints for META-INF/VAADIN/* and ClientViewConfig.class

* fix hints for deserializing file-routes properly

---------

Co-authored-by: Marcus Hellberg <marcus@vaadin.com>
Co-authored-by: Soroosh Taefi <taefi.soroosh@gmail.com>
@rbrki07 rbrki07 deleted the fix/add-resource-pattern-for-file-routes branch May 20, 2024 18:23
platosha added a commit that referenced this pull request May 21, 2024
)

* fix: add resource pattern for file-routes.json (#2396)

* fix: add resource pattern for file-routes.json

* apply suggestions from taefi

* Register hints for META-INF/VAADIN/* and ClientViewConfig.class

* fix hints for deserializing file-routes properly

---------

Co-authored-by: Marcus Hellberg <marcus@vaadin.com>
Co-authored-by: Soroosh Taefi <taefi.soroosh@gmail.com>

* import AvailableViewInfo from hilla

* import ClientViewMenuConfig from hilla instead of MenuData

---------

Co-authored-by: René Wilby <rbrki07@gmail.com>
Co-authored-by: Marcus Hellberg <marcus@vaadin.com>
Co-authored-by: Soroosh Taefi <taefi.soroosh@gmail.com>
Co-authored-by: Anton Platonov <platosha@gmail.com>
@rbrki07
Copy link
Contributor Author

rbrki07 commented May 23, 2024

Hi @taefi,
is there already an issue where you try to fix the problem when calling endpoints, as discussed here: #2396 (comment)? 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 java.base@21.0.2/java.lang.reflect.Method.acquireMethodAccessor(Method.java:77) ~[native-image-test:na]
        at java.base@21.0.2/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 java.base@21.0.2/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 java.base@21.0.2/java.lang.Thread.runWith(Thread.java:1596) ~[native-image-test:na]
        at java.base@21.0.2/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]

@marcushellberg
Copy link
Member

Ah. Good that you reminded of that. It's not fixed, but needs to be fixed #2461

@vaadin-bot
Copy link
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

No menu items in Hilla app when running as native compiled image
7 participants