mvysny / vaadin-boot

Boots your Vaadin app in embedded Jetty from your main() method quickly and easily
MIT License
17 stars 2 forks source link

Native #10

Open mvysny opened 1 year ago

mvysny commented 1 year ago

Native is cool these days. Add support for building with Graalvm; there's a Gradle plugin to build for native. Jetty and class auto discovery needs to be figured out.

mvysny commented 1 year ago

I've installed GraalVM and went through the tutorial at https://graalvm.github.io/native-build-tools/latest/gradle-plugin-quickstart.html - everything worked well.

Tried the same steps (agent+metadataCopy+nativeCompile) on https://github.com/mvysny/vaadin-boot-example-gradle, but unfortunately I'm getting

java.lang.NullPointerException
        at com.vaadin.flow.server.VaadinServlet.serveStaticOrWebJarRequest(VaadinServlet.java:431)
        at com.vaadin.flow.server.VaadinServlet.service(VaadinServlet.java:386)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
        at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
        at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:170)
        at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
        at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
        at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
        at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
        at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:578)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)

which means that VaadinServlet.staticFileHandler is null, which means that VaadinServlet.init() wasn't called. The startup time is instantaneous though - 17ms - which looks really promising.

I've tried to add the [Lorg.eclipse.jetty.servlet.ServletMapping; mapping as per https://javalin.io/2018/09/27/javalin-graalvm-example.html , didn't help. The tutorial looks old so it's probably of no value now. Maybe an updated tutorial of javalin+graalvm could also help.

I'm compiling Vaadin dev mode to native atm which is kind of dumb. I'll try the same thing but with prod mode.

I wonder whether GraalVM is actually compatible with Jetty's classpath scanning. Will study more. Perhaps an up-to-date jetty+graalvm article could help tremendously.

mvysny commented 1 year ago

Unfortunately the production build fails with the same exception.

Further things to check out:

mvysny commented 1 year ago

Debugged VaadinServlet in native a bit (by introducing my own MyServlet which extends VaadinServlet, and adding a bunch of stdout statements), found interesting info. VaadinServlet.init() actually does get called, however it soon bails out and does nothing, because Lookup is null. According to https://mvysny.github.io/vaadin-lookup-vs-instantiator/ that means that LookupServletContainerInitializer is not called, which means that Jetty classpath autodiscovery isn't working properly.

mvysny commented 1 year ago

Interesting related ticket which suggests that there might be some limitation in Jetty's classpath scanning: https://github.com/mvysny/vaadin-boot-example-maven/issues/1

mvysny commented 1 year ago

Could the Quick start be the solution? https://github.com/mvysny/vaadin-boot/issues/11

mvysny commented 1 year ago

Yup, QuickStart should help. Blocked by #11

mvysny commented 1 year ago

When running the native binary, QuickStart fails with

2023-03-16 13:32:47.747 [main] WARN org.eclipse.jetty.webapp.WebAppContext - Failed startup of context o.e.j.w.WebAppContext@1da6646c{/,resource:/webapp,STOPPED}{resource:/webapp}
java.lang.IllegalStateException: Bad Quickstart location
    at org.eclipse.jetty.quickstart.QuickStartConfiguration.preConfigure(QuickStartConfiguration.java:95)
    at org.eclipse.jetty.webapp.Configurations.preConfigure(Configurations.java:496)
    at org.eclipse.jetty.webapp.WebAppContext.preConfigure(WebAppContext.java:510)
mvysny commented 1 year ago

The reason for that is that context.getBaseResource() resolves to resource:/webapp of type URLResource; both URLResource.isDirectory() and URLResource.exists() return false. I've patched QuickStartConfiguration:94 and commented out the check. I've also passed in the path to quickstart-web.xml as ctx.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, new PathResource(new File("src/main/resources/webapp/WEB-INF/quickstart-web.xml")));, and now the native mode works.

I'll polish the native mode, document, file a feature request at Jetty.

mvysny commented 1 year ago

Blocked by https://github.com/eclipse/jetty.project/issues/9514