Skip to content

Wrapping up Jtwig inside Vertx TemplateEngine

Hello! When you develop a “classic” model-view-controller web application, you require to have a template engine for server-side rendering. Vertx framework provides several template engines as part of its wide ecosystem, including Apache Freemarker. In case you don’t like presented solutions and your favorite template engine is not implemented yet, you can DIY with TemplateEngine class from Vertx-Web. In this post I want to show how to do it by example of my favorite Jtwig template engine. NB this article is not intented to be a Jtwig tutorial, rather a guide to an implementation of Jtwig in Vertx principles.

How Vertx renders views

In order to get a rendered view (page) in Vertx, at the beginning you have to obtain TemplateEngine. There are several implementations, that are available for developers as parts of the Vertx ecosystem, including Vertx edition of the famous Apache Freemarker. But if your favorite template engine is not there, you can effortlessly build it yourself. Your journey will start with an implementation of io.vertx.ext.web.common.template.TemplateEngine interface. It provides a void method render() that has 3 arguments:

  1. Map with context data
  2. Filename for template file
  3. Async result handler with rendered template

What is important here to note, is that template engines render views not as strings, but as buffers. So, we need to watch out and convert result of string, created by template engine in vanilla Java to Vertx buffer. Let have a real example – implement the template engine for Jtwig.

Implementation: Jtwig template engine

In this section we will write an implementation of Jtwig template engine for Vertx. We concentrate on Vertx concepts, so our template will be incredibly dummy: just greets us with a provided name, like “Hello, Yuri”.

Step 1. Create a new project

Before you begin, let create a new project and add dependencies. I use Gradle in this example. We need 3 external libraries: Vertx Core, Vertx Web and Jtwig Core:

implementation 'io.vertx:vertx-core:3.8.3'
implementation 'io.vertx:vertx-web:3.8.3'
implementation 'org.jtwig:jtwig-core:5.87.0.RELEASE'

Next step is to create a template file

Step 2. Create a template

By default, Jtwig (and Vertx) looks for template files in classpath/resources/ folder. Also, a good idea is to create a separate folder for templates, like resources/template. NB this is not a post about twig syntax, so if you are not familiar with Jtwig and twig, feel free to look into official docs. But all what will do here is just create a simple greeting string:

Hello, {{name}}

As our goal is to prove a concept. Inside double curly braces, twig places variables. So in order to render this template with name, we supply key-value record "name":"...". NB what is cool about Jtwig is that it can work not only with variable names, but also with Java methods, so if you provide an object with getters, you can use {{object.getName()}}.

Step 3. Implement a template engine

Let observe which components uses Jtwig. You need to follow these steps:

  1. Obtain JtwigTemplate from file
  2. Create JtwigModel with data
  3. Call template.render(model) to get a rendered view

First step is to get JtwigTemplate. We will write a separate method to obtain from the specified template file:

private JtwigTemplate getTemplate(String filename){
    return JtwigTemplate.classpathTemplate(filename);
}

Fairly simple, yes? Next, is to implement render method for Vertx template engine:

public void render(Map<String, Object> data, 
String filename, 
Handler<AsyncResult<Buffer>> handler) {
 try {
        JtwigTemplate template = getTemplate(filename);
        JtwigModel model = JtwigModel.newModel(data);
        Buffer buffer = Buffer.buffer(template.render(model));
        handler.handle(Future.succeededFuture(buffer));
  } catch (Exception ex){
        ex.printStackTrace();
        handler.handle(Future.failedFuture(ex));
  }
}

Here we follow discussed steps. I would like to note, that I use exception handling here. Jtwig does not throw checked exceptions, altough it is good idea to catch runtime exceptions and fail the async result in case of problem. Please also observe, that we need to provide the rendered model in a form of Vertx buffer, not just as a string. Full code for this class is below:

class JtwigTemplateEngine implements TemplateEngine {

    private JtwigTemplate getTemplate(String filename){
        return JtwigTemplate.classpathTemplate(filename);
    }

    @Override
    public void render(Map<String, Object> data, 
    			    String filename, 
    			    Handler<AsyncResult<Buffer>> handler) {
        try {
            JtwigTemplate template = getTemplate(filename);
            JtwigModel model = JtwigModel.newModel(data);
            Buffer buffer = Buffer.buffer(template.render(model));
            handler.handle(Future.succeededFuture(buffer));
        } catch (Exception ex){
            ex.printStackTrace();
            handler.handle(Future.failedFuture(ex));
        }
    }

    @Override
    public boolean isCachingEnabled() {
        return false;
    }

}

Step 4. Create an HttpVerticle

The last step is to write a verticle that will use this template engine. Create a new HttpVerticle and override its start() method:

@Override
public void start(Promise<Void> promise) throws Exception {
    JtwigTemplateEngine templateEngine  = new JtwigTemplateEngine();
    HttpServer server = vertx.createHttpServer();
    Router router = Router.router(vertx);
    router.get("/").handler(context->{
        Map<String, Object> data = new HashMap<>();
        data.put("name", "Yuri");
        templateEngine.render(data, "template/hello.twig", res->{
            if (res.succeeded()){
                context.response().end(res.result());
            }
        });
    });
    server.requestHandler(router);
    server.listen(4567, res->{
        if (res.succeeded()){
            promise.complete();
        } else {
            promise.fail(res.cause());
        }
    });
}

The code snippet above is self-explanatory. We start from obtaining instances of important components – JtwigTemplateEngine, HttpServer and Router. Next, we register a handler for / GET route. We specify data object (of course, in a real-life application, we would have more parameters) and pass it to render() method. Our app is ready to go, the only missing thing is to deploy it.

Step 5. Deploy and test

In this final subsection we write main() method to create Vertx instance and deploy HttpVerticle. Take a look on the code snippet below:

public static void main(String[] args) {
    Vertx vertx = Vertx.vertx();
    vertx.deployVerticle(new HttpService());
}

This is an entry point for our application. You can place it inside a separate Application class – this makes sence if you have to, for example, obtain a configuration on startup, however in this simple example it will not hurt to place it inside the verticle itself. Run this application and access in your browser localhost:4567/. The result should be similar to this:

Of course, this is a very simple template, but in a same time this post does not pretend to be a comprehensive introduction to JTwig. I advise you to take a look in the official docs, in case you are not familiar with Jtwig and Twig.

Conclusion

As you have observed, this is not a rocket science to wrap your favorite engine in Vertx paradigm. In this post we discovered essential steps to implement Jtwig template engine for Vertx. The most work is done inside the implementation of TemplateEngine interface. Hope, this will help you to understand better MVC development with Vertx. Have a nice day!

Copy link
Powered by Social Snap