Sven Ruppert

Ramp up in a second with RedHat Undertow

This is a nano project starter for Vaadin projects. It’s perfect for micro UIs packed as a fat jar in a docker image. The base project will be used in some of the tutorial implementations as well.

Target of this project

The target of this project is a minimal ramp-up time for a first hello world. Why we need one more HelloWorld? Well, the answer is quite easy. If you have to try something out, or you want to make a small POC present something, there is no time and budget to create a demo project. You don’t want to copy paste all small things together. Here you will get a HelloWorld-project that will give you everything you need quickly.

Clone the repo and start editing the class VaadinApp or CoreUIService. Nothing more.

How does it work?

This project does not use any additional maven plugins or technologies.

Here we are using the plain undertow as the Servlet container. https://github.com/undertow-io/undertow

However, let’s start from the beginning.

Start the Servlet container (Java)

The CoreUIService class ramps up the Container.

Here all the basic stuff is done. The start will initializing a ServletContainer at port 8080. The WebApp will be deployed as ROOT.war. The following code will show how to ramp up an Undertow from scratch.

  public void startup() {
    final ClassLoader classLoader = CoreUIService.class.getClassLoader();
    DeploymentInfo servletBuilder
        = Servlets.deployment()
                  .setClassLoader(classLoader)
                  .setContextPath("/")
                  .setDeploymentName("ROOT.war")
                  .setDefaultEncoding("UTF-8")
                  .setResourceManager(new ClassPathResourceManager(classLoader, "META-INF/resources/"))
                  .addServletContainerInitializer(
                      new ServletContainerInitializerInfo(RouteRegistryInitializer.class ,
                                                          setOfRouteAnnotatedClasses())
                      )
                  .addListener(Servlets.listener(ServletDeployer.class));

    final DeploymentManager manager = Servlets
        .defaultContainer()
        .addDeployment(servletBuilder);
    manager.deploy();

    try {
      PathHandler path = path(redirect("/"))
          .addPrefixPath("/" , manager.start());
      Undertow u = Undertow.builder()
                           .addHttpListener(valueOf(getProperty(CORE_UI_SERVER_PORT ,
                                                                CORE_UI_SERVER_PORT_DEFAULT)) ,
                                            getProperty(CORE_UI_SERVER_HOST ,
                                                        CORE_UI_SERVER_HOST_DEFAULT)
                           )
                           .setHandler(path)
                           .build();
      u.start();

      u.getListenerInfo().forEach(e -> logger().info(e.toString()));

      undertow = success(u);
    } catch (ServletException e) {
      logger().warning(e.getMessage());
      undertow = failure(e.getMessage());
    }
  }

One crucial and Vaadin specific part of this code is listed below.

.setResourceManager(new new ClassPathResourceManager(classLoader, "META-INF/resources/"))

If you are using the core Undertow, the default ResourceManager is a dummy implementation. This will to a few 404 if Vaadin Flow will try to load resources that are part of a jar. To get this solved, we need to declare a prefix for the ClassPathResourceManager The prefix is "META-INF/resources/". This path is not needed for ServletContainer that are implementing and actively using the Servlet Spec 4. You will find in chapter 4.6 the definition of this folder.

But if you need your own paths included, you can implement the interface ResourceManager. As a sample you will find an implementation below. (or in the class VaadinFlowResourceManager) The VaadinFlowResourceManager can handle the Vaadin specific resources and webjars.

  @Override
  public Resource getResource(final String path) throws IOException {
    String modPath = path;
    if (modPath.startsWith("/")) {
      modPath = path.substring(1);
    }
    if (modPath.startsWith("webjars")) {
      modPath = "META-INF/resources/" + modPath;
    }
    if (modPath.startsWith("VAADIN/static/client")) {
      modPath = "META-INF/resources/" + modPath;
    }
    final String realPath = prefix + modPath;
    final URL resource = classLoader.getResource(realPath);
    if (resource == null) {
      return null;
    } else {
      return new URLResource(resource , path);
    }
  }

After this, you can start the app invoking the main method.

The UI itself

The UI itself doesn’t contain many elements. There is only a button you can click. For every click, the counter will be increased. The component will be registered under the root path, based on the Annotation @Route("") For more pieces of information about the routing itself have a look at https://vaadin.com/docs/flow/routing/tutorial-routing-annotation.html

@Route("")
public class VaadinApp extends Composite<Div> {

  private final Button         btnClickMe   = new Button("click me");
  private final Span           lbClickCount = new Span("0");
  private final VerticalLayout layout       = new VerticalLayout(btnClickMe, lbClickCount);

  private int clickcount = 0;

  public VaadinApp() {
    btnClickMe.addClickListener(event -> lbClickCount.setText(valueOf(++clickcount)));

    //set the main Component
    System.out.println("setting now the main ui content..");
    getContent().add(layout);

  }
}

Happy Coding.

Vaadin is an open-source framework offering the fastest way to build web apps on Java backends
GET STARTED

Comments (1)