Using Webjars with Vaadin

Matti Tahvonen
Matti says about web apps…
3 min readMay 28, 2015

Webjars are a handy way to consume various JS libraries in JVM applications. Maven will handle the dependencies and typically those are really easy serve from your web applications. With Servlet 3 containers you don’t even need a specific servlet for that, but typical Vaadin applications have a small issue with this. Many (but not all) Vaadin apps maps VaadinServlet to catch all requests coming to the web application.

If you wish to use Webjars with Vaadin application with the problematic catch-them-all-strategy you can naturally map for example the Servlet 2 helper to explicitly catch requests from “/webjars/*”. Another possibility is to just forward the requests form the same url “back” to the default dispatcher of the server. I’m not sure if this “hack” conforms to some specification, but pretty much all servers seem to publish their default dispatcher with a name “default”. A simple servlet to pass webjar requests back to default servlet/server would look like this:

/**
* Forward webjar requests to default dispatcher that can handle
* servlet 3 style META-INF/resources. Not sure if this is
* based on any standard, but works at least in jetty, tomcat,
* wildlfy, glassfish.
*/
@WebServlet(urlPatterns = "/webjars/*")
public static class WebjarServlet extends HttpServlet {

private RequestDispatcher defaultDispatcher;

@Override
public void init() throws ServletException {
super.init();
defaultDispatcher = getServletContext().
getNamedDispatcher("default");
}

@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
defaultDispatcher.forward(request, response);
}

}

After that you can access Webjars like with any other JVM application, even with a VaadinServlet mapped to “/*”.

Wiring resources to Vaadin app

Next part is to actually use the JS/CSS from your webjars, in your Vaadin application. Vaadin 7 introduced JavaScript and StyleSheet annotations to easily add this sort of resources, but they are not particularly handy for this purpose. Instead it is easier just to introduce a BootstrapListener to modify the “host page” where Vaadin application is living:

@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();
VaadinServletService.getCurrent().addSessionInitListener(
new SessionInitListener() {

@Override
public void sessionInit(SessionInitEvent event) throws ServiceException {
VaadinSession.getCurrent().addBootstrapListener(
new BootstrapListener() {

@Override
public void modifyBootstrapFragment(
BootstrapFragmentResponse response) {
}

@Override
public void modifyBootstrapPage(
BootstrapPageResponse response) {
// Update the bootstrap page
Document document = response.getDocument();
Element head = document.getElementsByTag("head").get(0);
addWebJarResource(document, head, "jquery/1.11.1/jquery.min.js");
addWebJarResource(document, head, "jquery-ui/1.11.0/jquery-ui.min.js");
addWebJarResource(document, head, "jQuery-Timepicker-Addon/1.5.0/jquery-ui-timepicker-addon.min.js");
addWebJarResource(document, head, "jquery-ui/1.11.0/jquery-ui.theme.min.css");
addWebJarResource(document, head, "jQuery-Timepicker-Addon/1.5.0/jquery-ui-timepicker-addon.min.css");

}
void addWebJarResource(Document d, Element head, String id) {
if(id.endsWith(".js")) {
Element s = d.createElement("script");
s.attr("src", "webjars/"+ id);
s.attr("type", "text/javascript");
head.appendChild(s);
} else if (id.endsWith(".css")) {
Element s = d.createElement("link");
s.attr("href", "webjars/"+id);
s.attr("rel", "stylesheet");
head.appendChild(s);
}
}
});
}
});
}

}

The above can also be implemented using a HttpSessionListener in case you don’t have a servlet (e.g. Spring or CDI). And to complete the above example you can e.g. use simple JS call to convert a basic TextField into jquery-ui-datepicker:

final TextField toBeTimePickerField = new TextField();
toBeTimePickerField.setId("tp-field");
JavaScript.eval("$('#tp-field').datetimepicker();");

Further enhancement possibilities

We should definitely create an add-on or enhancement to the Vaadin core that would make the VaadinServlet serve static files from Webjars automatically. A more helpful utility library would be to create a tool that would introduce the resources with simple annotations attached to UI classes.

I quickly prototyped this a bit in a branch of Viritin. The solution is rather rudimentary (should use requireJS and support “current” as version and also support CDN hosting), but seems to work pretty well in a test project like this (with SNAPSHOT build of Viritin from webjars branch + “org.webjars:jquery:1.11.1” in pom.xml):

@Theme("valo")
// Introduce teh webjar resource to be added to the host page
@WebjarResource("jquery/1.11.1/jquery.min.js")
public class DemoUI extends UI {

@Override
protected void init(VaadinRequest request) {
Label label = new Label("Click to say hi with JQuery");
label.setId("labeli");

// Use JS from the webjar (jquery in this case)
JavaScript.eval("$( \"#labeli\" ).click(function() {"
+ "alert( \"Handler for .click() called.\" );"
+ "});");

setContent(label);

}

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

}

No need to write a BootstrapListener yourself. Pretty simple? Would this kind of feature be handy for you? Should I release a helper as such? If you have enhancement suggestions or experiences with Vaadin + Webjars, please let us know!

--

--