Search code examples
javascriptjavajakarta-eevaadinvaadin7

How to setup a RequestHandler for images with VaadinServlet


I want to have a RequestHandler to be able to dynamically create images at say myDomain/images/id8938748.jpg and at the same handle all other traffic through the standard VaadinServlet. To do this I understand that I need to put the RequestHandler in the VaadinServlet as explained here by extending it such as:

public class MyCustomServlet extends VaadinServlet 
{
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
        {
            getService().addSessionInitListener(new SessionInitListener() 
            {
                @Override
                public void sessionInit(SessionInitEvent event) throws ServiceException 
                {
                    event.getSession().addRequestHandler(myCustomRequestHandlerForImages);
                }
            });
        }
}

My problem is how do you register the new MyCustomServlet? I looked at doing it in the web.xml but I just can't seem to get it right.

My RequestHandler is simply:

@Override
public boolean handleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException 
{
    System.out.println("Test it's being called");
}

For the web.xml I have:

<servlet>
    <servlet-name>MyCustomVaadinServlet</servlet-name>
    <servlet-class>
        com.test.MyCustomVaadinServlet
    </servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>MyCustomVaadinServlet</servlet-name>
    <url-pattern>/images/*</url-pattern>
</servlet-mapping>

I then have the other Servlet defined in the UI code with annotations:

public class MyUI extends UI
{
  @WebServlet(value = "/*", asyncSupported = true)
  @VaadinServletConfiguration(productionMode = false, ui = MyUI.class, widgetset = "com.MyWidgetSet")
    public static class Servlet extends VaadinServlet 
  {
  }
  ...
}

When I call domain itself it all works as expected and my Vaadin application works. But when I call domain/images or domain/images/randomText I get an empty page and the System.out.println statements are NOT outputted. Basically the RequestHandler is NOT called.

I have also tried things like mapping to /images/* and /app/* but that didn't make any difference...


Solution

  • If you display those images in your Vaadin UI by using for example the Image component, you could use a Resource. There are several resource implementations available:

    • ThemeResource: add your file (e.g. image) to your theme and use this to reference to the image.
    • ÈxternalResource: A file on a URL.
    • ClassResource: A file on your classpath, Vaadin servlet serves the file to the browser.
    • FileResource: A file on the filesystem of the server. Vaadin servlet serves the file to the browser.
    • StreamResource: Generate your file from an InputStream. Vaadin servlet serves the file to the browser.

      Image image = new Image();
      image.setSource(new ThemeResource("path/is/relative/to/the/theme/folder.png"));
      

    A valid alternative is also to do so that you create a regular servlet that generates and serves the files. Then you cannot map the Vaadin Servlet to the context root anymore. Then you could use an ExternalResource to reference to the files in your Vaadin code:

    Image image = new Image();
    image.setSource(new ExternalResource("/images/id8938748.jpg"));
    

    Your servlet mapping would look for example as follows:

    <servlet-mapping>
      <servlet-name>MyImageServlet</servlet-name>
      <url-pattern>/images/*</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
      <servlet-name>MyVaadinServlet</servlet-name>
      <url-pattern>/ui/*</url-pattern>
    </servlet-mapping>
    
    <servlet-mapping>
      <servlet-name>MyVaadinServlet</servlet-name>
      <url-pattern>/VAADIN/*</url-pattern>
    </servlet-mapping>