/ KOTLIN, VAADIN

Feedback on customizing Vaadin HTML template

Last week, my post was about how you could customize the default Vaadin HTML template so you could add a lang attribute for example. I didn’t ask for feedback, but I got it anyway, so let’s use the occasion to analyze it.

First, I must admit that my solution was not Vaadin-esque as I used AOP to get the job done. My friend Matti Tahvonen from the Vaadin team was kind enough to provide not only one but 2 alternatives to my proposal. Thanks Matti!

Alternative solutions

The first solution - also the one I got the last, was submitted by AMahdy AbdElAziz:

JavaScript.eval("document.querySelector('html').setAttribute('lang', 'fr')")

Evil but it works and couldn’t be simpler!

The second solution uses Matti’s own framework - Viritin, to achieve that. I haven’t looked at the framework yet (but it’s in my todo list) so I cannot comment it but here is the snippet:

@Theme("mytheme")
public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        // HtmlElementPropertySetter is part of Viritin add-on https://vaadin.com/directory=!addon/viritin
        HtmlElementPropertySetter heps = new HtmlElementPropertySetter(this);
        heps.setProperty("//html", "lang", "fr");
        setContent(new Label("Hello lang attr"));
    }

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

The standard Vaadin-esque server-side way is a bit more convoluted:

@Theme("mytheme")
public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        setContent(new Label("Hello lang attr"));
    }

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
        @Override
        protected void servletInitialized() throws ServletException {
            super.servletInitialized();
            getService().addSessionInitListener(e ->
                e.getSession().addBootstrapListener(new BootstrapListener() {
                    @Override
                    public void modifyBootstrapFragment(
                            BootstrapFragmentResponse response) {
                        // NOP, this is for portlets etc
                    }
                    @Override
                    public void modifyBootstrapPage(BootstrapPageResponse response) {
                        response.getDocument().child(0).attr("lang", "fr");
                    }
                })
            );
        }
    }
}

However, this one I can analyze and comment :-)

Flow decomposition

Note: readers who are more interested in possoble improvements can skip directly to that section.

The first part is quite easy. It just registers a new SessionInitListener (the proposed code uses a lambda to do that):

Add Session Initialization Listener

The second part happens when a request is made and Vaadin notices a new session must be created:

handlerequest

The end of the previous sequence diagram is designed in a generic way in Vaadin. In our specific case, it’s rendered as such:

Add Bootstrap Listener

Improvements

I think a couple of improvements can be made.

  • The code is awfully verbose - even using Java 8’s lambda
  • Two objects are created each time a session is initialized, the session listener and the bootstrap listener
  • Setting the servlet class as an internal class of the UI sends shiver down my spine. Though I understand this is a Gist, this is unfortunately what you get by using the Vaadin Maven archetype. This is very far from the Single-Responsibility Principle.

Since my initial example uses both Spring Boot and Kotlin, here’s my version:

@Configuration
open class AppConfiguration {

    // ... Abridged for readability's sake

    @Bean
    open fun vaadinServlet() = CustomVaadinServlet{ event: SessionInitEvent ->
        event.session.addBootstrapListener(object : BootstrapListener {
            override fun modifyBootstrapFragment(response: BootstrapFragmentResponse) {
                // NOP, this is for portlets etc
            }
            override fun modifyBootstrapPage(response: BootstrapPageResponse) {
                response.document.child(0).attr("lang", "fr")
            }
        })
    }
}

class CustomVaadinServlet(private val listener: (SessionInitEvent) -> Unit) : SpringVaadinServlet() {
    override fun servletInitialized() {
        super.servletInitialized();
        service.addSessionInitListener(listener)
    }
}

With Spring Boot, I can manage the SessionInitListener as a singleton-scoped Bean. By passing it as a servlet parameter, I can create only a single instance of SessionInitListener and BootstrapListener each. Of course, it’s only possible because the language value is set in stone.

Thanks to Kotlin, I co-located the overriden servlet class within the configuration file but outside the configuration class. Since the servlet is used only by the configuration, it makes sense to put them together…​ but not too much.

Finally, note that SessionInitListener is a functional interface, meaning it has a single method. This single method is the equivalent of a function taking a SessionInitEvent and return nothing. In Kotlin, the signature is (SessionInitEvent) → Unit. Instead of creating an anonymous inner class, I preferred using the function. It’s not an improvement, but a more functional alternative. At runtime, both alternatives will allocate the same amount of memory.

The complete source code can be found on Github in the manage-lang branch.

Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a technologist focusing on cloud-native technologies, DevOps, CI/CD pipelines, and system observability. His focus revolves around creating technical content, delivering talks, and engaging with developer communities to promote the adoption of modern software practices. With a strong background in software, he has worked extensively with the JVM, applying his expertise across various industries. In addition to his technical work, he is the author of several books and regularly shares insights through his blog and open-source contributions.

Read More
Feedback on customizing Vaadin HTML template
Share this