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

Improve start speed via QuickStartWebApp #11

Closed
mvysny opened this issue Mar 13, 2023 · 8 comments
Closed

Improve start speed via QuickStartWebApp #11

mvysny opened this issue Mar 13, 2023 · 8 comments
Assignees
Labels
enhancement New feature or request

Comments

@mvysny
Copy link
Owner

mvysny commented Mar 13, 2023

Jetty's QuickStartWebApp mechanism allow to generate a Jetty config file in build time, which would then remove the need to waste time in classpath scanning in runtime, allowing for far faster startup times.

Couple of links:

@mvysny mvysny self-assigned this Mar 13, 2023
@mvysny mvysny added the enhancement New feature or request label Mar 13, 2023
@mvysny
Copy link
Owner Author

mvysny commented Mar 14, 2023

The org.eclipse.jetty:jetty-quickstart Maven artifact contains related classes, both for Jetty 10 and 11. Documentation:

@mvysny
Copy link
Owner Author

mvysny commented Mar 14, 2023

Sadly mvn -C jetty:effective-web-xml fails with Packaging type [jar] is unsupported.

@mvysny
Copy link
Owner Author

mvysny commented Mar 14, 2023

I've filed a feature request for the Maven plugin at jetty/jetty.project#9497 . Meanwhile, perhaps I could experiment with the QuickStartGeneratorConfiguration.generateQuickStartWebXml() function in Gradle, perhaps take a look at Maven plugin sources: https://github.com/eclipse/jetty.project/tree/jetty-11.0.x/jetty-maven-plugin

@mvysny
Copy link
Owner Author

mvysny commented Mar 14, 2023

I've invoked the generator myself; the speed improvement is not that ground-breaking: 822ms vs 1200ms startup time. Perhaps that's because the classpath scanning runs in parallel on all cores: the performance improvement could be much bigger on a single-core machines (or single-core docker images).

The code which generated the file:

    private static final File quickstartXml = new File("src/main/resources/webapp/WEB-INF/quickstart-web.xml");
            context.setAttribute(ExtraXmlDescriptorProcessor.class.getName(), new ExtraXmlDescriptorProcessor());
            try (OutputStream out = new BufferedOutputStream(new FileOutputStream(quickstartXml))) {
                new QuickStartGeneratorConfiguration().generateQuickStartWebXml(context, out);
            }

Jetty server needs to be started to perform the classpath detection. To configure WebAppContext to reuse the generated quickstart-web.xml, configure the WebAppContext as follows:

            context.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART);
            context.addConfiguration(new QuickStartConfiguration());

@mvysny
Copy link
Owner Author

mvysny commented Mar 15, 2023

The best solution would be if the quickstart-web.xml file could be generated at compile-time, by performing classpath scanning on all dependency jars and in-project files. That is unfortunately not possible at the moment, blocked by jetty/jetty.project#9497 .

There's no one-fits-all xml unfortunately since it lists all Vaadin Routes for example, which means that the xml needs to be updated whenever there's a new route; also the xml differs for dev mode and production mode (since production mode doesn't run DevModeStartupListener).

That being said, I can still add some basic support for quickstart since it will be valuable for native builds.

The src/main/resources/webapp/WEB-INF/quickstart-web.xml file for reference follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" metadata-complete="false" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5.0">
  <context-param>
    <param-name>org.eclipse.jetty.containerInitializers</param-name>
    <param-value><![CDATA[
    "ContainerInitializer{com.vaadin.flow.server.startup.WebComponentExporterAwareValidator,interested=[],applicable=[],annotated=[]}",
    "ContainerInitializer{org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer,interested=[org.atmosphere.container.JSR356Endpoint],applicable=[],annotated=[]}",
    "ContainerInitializer{com.vaadin.flow.server.startup.RouteRegistryInitializer,interested=[com.example.MainView],applicable=[],annotated=[]}",
    "ContainerInitializer{com.vaadin.flow.server.startup.WebComponentConfigurationRegistryInitializer,interested=[com.vaadin.flow.component.WebComponentExporterFactory$DefaultWebComponentExporterFactory],applicable=[],annotated=[]}",
    "ContainerInitializer{org.eclipse.jetty.websocket.jakarta.client.JakartaWebSocketShutdownContainer,interested=[],applicable=[],annotated=[]}",
    "ContainerInitializer{com.vaadin.flow.server.startup.VaadinAppShellInitializer,interested=[com.example.AppShell],applicable=[],annotated=[]}",
    "ContainerInitializer{com.vaadin.flow.server.startup.LookupServletContainerInitializer,interested=[com.vaadin.flow.di.LookupInitializer$AppShellPredicateImpl, com.vaadin.flow.router.DefaultRoutePathProvider, com.vaadin.flow.di.LookupInitializer$StaticFileHandlerFactoryImpl, com.vaadin.flow.di.LookupInitializer, com.vaadin.flow.di.LookupInitializer$ResourceProviderImpl, com.vaadin.flow.server.startup.DefaultApplicationConfigurationFactory],applicable=[],annotated=[]}",
    "ContainerInitializer{com.vaadin.flow.server.startup.AnnotationValidator,interested=[],applicable=[],annotated=[]}",
    "ContainerInitializer{org.atmosphere.cpr.ContainerInitializer,interested=[],applicable=[],annotated=[]}",
    "ContainerInitializer{com.vaadin.flow.server.startup.ErrorNavigationTargetInitializer,interested=[com.vaadin.flow.router.RouteNotFoundError, com.vaadin.flow.router.InternalServerError],applicable=[],annotated=[]}",
    "ContainerInitializer{org.atmosphere.cpr.AnnotationScanningServletContainerInitializer,interested=[org.atmosphere.annotation.BroadcasterCacheServiceProcessor, org.atmosphere.annotation.AtmosphereHandlerServiceProcessor, org.atmosphere.annotation.BroadcasterListenerServiceProcessor, org.atmosphere.annotation.AtmosphereServiceProcessor, org.atmosphere.annotation.BroadcasterCacheListenererviceProcessor, org.atmosphere.annotation.AtmosphereFrameworkServiceProcessor, org.atmosphere.annotation.AsyncSupportListenerServiceProcessor, org.atmosphere.annotation.WebSocketFactoryServiceProcessor, org.atmosphere.annotation.BroadcasterServiceProcessor, org.atmosphere.annotation.EndpointMapperServiceProcessor, org.atmosphere.annotation.BroadcasterFactoryServiceProcessor, org.atmosphere.annotation.ManagedServiceProcessor, org.atmosphere.annotation.UUIDProviderServiceProcessor, org.atmosphere.annotation.AtmosphereResourceListenerServiceProcessor, org.atmosphere.annotation.BroadcasterCacheInspectorServiceProcessor, org.atmosphere.annotation.AtmosphereInterceptorServiceProcessor, org.atmosphere.annotation.MeteorServiceProcessor, org.atmosphere.annotation.WebSocketProtocolServiceProcessor, org.atmosphere.annotation.BroadcastFilterServiceProcessor, org.atmosphere.annotation.WebSocketProcessorServiceProcessor, org.atmosphere.annotation.AsyncSupportServiceProcessor, org.atmosphere.annotation.WebSocketHandlerServiceProcessor, org.atmosphere.annotation.AtmosphereResourceFactoryServiceProcessor],applicable=[],annotated=[]}"]]></param-value>
  </context-param>
  <context-param>
    <param-name>org.eclipse.jetty.originAttribute</param-name>
    <param-value>origin</param-value>
  </context-param>
  <listener origin="DefaultsDescriptor(jar:file:/home/mavi/.gradle/caches/modules-2/files-2.1/org.eclipse.jetty/jetty-webapp/11.0.14/15e01322cc54172e474357f8ae8dd4ec15e5ed09/jetty-webapp-11.0.14.jar!/org/eclipse/jetty/webapp/webdefault.xml):0">
    <listener-class>org.eclipse.jetty.servlet.listener.IntrospectorCleaner</listener-class>
  </listener>
  <listener origin="@WebListener(com.example.Bootstrap):1">
    <listener-class>com.example.Bootstrap</listener-class>
  </listener>
  <listener origin="@WebListener(com.vaadin.flow.server.startup.ServletContextListeners):2">
    <listener-class>com.vaadin.flow.server.startup.ServletContextListeners</listener-class>
  </listener>
  <listener origin="@WebListener(com.vaadin.flow.server.startup.VaadinAppShellInitializer):3">
    <listener-class>com.vaadin.flow.server.startup.VaadinAppShellInitializer</listener-class>
  </listener>
  <listener>
    <listener-class>org.eclipse.jetty.websocket.jakarta.client.JakartaWebSocketShutdownContainer</listener-class>
  </listener>
</web-app>

I'll add both:

  1. A way to generate this xml file. At the moment the file will be generated by a call to a special method at runtime, it's not perfect but will work until Maven plugin add support for jar projects in :effective-web-xml jetty/jetty.project#9497 is fixed. Alternatively we can generate the XML file ourselves, by performing a classpath scan ourselves.
  2. A support to activate the file if it's on the classpath. Note that the file differs for dev mode and production mode, therefore we should expect the file to target production mode and ignore the file for dev mode by default. I propose a three-state QuickStart enum: Off, Production, Always. Off would be the default and would work as currently - always doing classpath scanning. Production would only activate the QuickStart during production mode and would fail if the quickstart xml file is missing.

mvysny added a commit that referenced this issue Mar 15, 2023
mvysny added a commit that referenced this issue Mar 15, 2023
@mvysny
Copy link
Owner Author

mvysny commented Mar 15, 2023

Showstopper: if I add the org.eclipse.jetty:jetty-quickstart artifact on classpath by default, the QuickStartConfiguration is enabled automatically by default, and then interferes with Jetty. If ctx.baseResource = EmptyResource.INSTANCE is used, then QuickStartConfiguration fails at 95 with IllegalStateException("Bad Quickstart location"). ctx.removeConfiguration(QuickStartConfiguration::class.java) doesn't help.

@mvysny
Copy link
Owner Author

mvysny commented Mar 15, 2023

Need to revert and remove the jetty-quickstart. Instead I'll document the steps necessary.

@mvysny mvysny closed this as completed in f7d0f0f Mar 15, 2023
@mvysny mvysny reopened this Mar 16, 2023
@mvysny
Copy link
Owner Author

mvysny commented Mar 16, 2023

Reopening: the new Jetty Maven plugin needs to be tested; README would benefit from the plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant