Hosting files when Vaadin application is running in root context

https://vaadin.com/docs/-/part/framework/application/application-environment.html shows how to run vaadin app in root context with @WebServlet(value = “/*”, asyncSupported = true). However this makes it difficult to serve files like robots.txt or sitemap.xml, as the vaadin application seems to capture all requests. In what way should these files be served?

You can implement a custom RequestHandler to serve files (from classpath for example).
https://vaadin.com/docs/-/part/framework/advanced/advanced-requesthandler.html

Another way is to use a javax.servlet.Filter mapped only on the resources you want to serve.

HTH
Marco

The RequestHandler method doesn’t work properly, because the first time user accesses the URL it still loads the UI. Only on the second attempt it serves the file.

As for the filter approach, i’m not sure how it would work since i would still have the /* mapping for the Vaadin application and it would still capture everything. Is there any example on how it should be configured?

Hi,
RequestHandler works fine, but you must add it directly in VaadinService not on VaadinSession

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {

        @Override
        protected VaadinServletService createServletService(DeploymentConfiguration deploymentConfiguration) throws ServiceException {
            VaadinServletService service = new VaadinServletService(this, deploymentConfiguration) {
                @Override
                protected List<RequestHandler> createRequestHandlers() throws ServiceException {
                    List<RequestHandler> h = super.createRequestHandlers();
                    h.add((session, request, response) -> {
                        if ("/rhexample".equals(request.getPathInfo())) {
                            // Generate a plain text document
                            response.setContentType("text/plain");
                            response.getWriter().append(
                                "Here's some dynamically generated content from request handler.\n");
                            response.getWriter().format(Locale.ENGLISH,
                                "Time: %Tc\n", new Date());
                            return true; // We wrote a response
                        }
                        return false; //
                    });
                    return h;
                }
            };
            service.init();
            return service;
        }
    }

For the filter map it only on the needed path; for example you can serve http://localhost/other with something like this

    @WebFilter(urlPatterns = "/other")
    public static class MyFilter implements Filter {

        // other methods omitted

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            response.setContentType("text/plain");
            response.getWriter().append(
                "Here's some dynamically generated content from filter.\n");
            response.getWriter().format(Locale.ENGLISH, "Time: %Tc\n", new Date());
        }

    }

HTH
Marco

Thanks, that RH example you posted works great. Maybe the documentation could be clearer about this.