Skip to main content

Template engine integration

In this example we will implement template engines in ActiveJ applications. The example shows how to create a simple Poll app which creates new polls with a custom title, description, and options. Each new poll gets a unique generated link that leads to the page where you can vote.

See how simple it is to implement such features using ActiveJ: the embedded application server has only about 100 lines of code with no additional xml configurations. In this example we used Mustache as a template engine.

Here we will consider only the ApplicationLauncher class, which is the main class of the application. You can find full example sources on GitHub

Creating launcher#

The ApplicationLauncher launches our application, takes care of routing and proper content generation on HTML pages. We will extend ActiveJ's HttpServerLauncher to manage the application's lifecycle:

note

In this example we omit error handling to keep everything brief and simple.

public final class ApplicationLauncher extends HttpServerLauncher {
private static ByteBuf applyTemplate(Mustache mustache, Map<String, Object> scopes) {
ByteBufWriter writer = new ByteBufWriter();
mustache.execute(writer, scopes);
return writer.getBuf();
}
@Provides
PollDao pollRepo() {
return new PollDaoImpl();
}

Let's have a closer look at the launcher. It contains two methods:

  • applyTemplate(Mustache mustache, Map<String, Object> scopes) fills the provided Mustache template with the given data through a ByteBuf - a more efficient implementation of ByteBuffer;
  • pollRepo() provides the business logic of our app. The @Provides annotation means it's done through ActiveJ Inject DI.

Next, we provide AsyncServlet:

public final class ApplicationLauncher extends HttpServerLauncher {
private static ByteBuf applyTemplate(Mustache mustache, Map<String, Object> scopes) {
ByteBufWriter writer = new ByteBufWriter();
mustache.execute(writer, scopes);
return writer.getBuf();
}
@Provides
PollDao pollRepo() {
return new PollDaoImpl();
}

In the AsyncServlet we create three Mustache objects, one for each HTML page. To define routing, we create a RoutingServlet. You may notice that the routing approach resembles Express.js. In the example above we've added the mapping to the homepage by using the map method.

Methodmap(@Nullable HttpMethod method, String path, AsyncServlet servlet) adds a route to the RoutingServlet:

  • method is one of the HTTP methods (GET, POST and so on)
  • path is the path on the server
  • servlet defines the logic of request processing. If you need to get some data from the request while processing you can use:
  • request.getPathParameter(String key)/request.getQueryParameter(String key) (see example of query parameter usage) to provide the key of the needed parameter and receive back a corresponding String
  • request.getPostParameters() to get a Map of all request parameters

In this request we get all current polls and info about them in order to generate listPolls page.

Let's add one more request:

.map(GET, "/poll/:id", request -> {
int id = Integer.parseInt(request.getPathParameter("id"));
return HttpResponse.ok200()
.withBody(applyTemplate(singlePollView, map("id", id, "poll", pollDao.find(id))));
})

This request returns a page with a poll which id was specified in the path. Pay attention to the provided path /poll/:id syntax. : states that the following characters until the next / are a variable; in this case, its keyword is id. This way you don't have to map each poll's id to a different request.

The next requests with /create, /vote, /add and /delete paths take care of providing a page for creating new polls, voting, adding created polls to the pollDao and deleting them from it respectively:

.map(GET, "/create", request ->
HttpResponse.ok200()
.withBody(applyTemplate(singlePollCreate, emptyMap())))
.map(POST, "/vote", loadBody()
.serve(request -> {
Map<String, String> params = request.getPostParameters();
String option = params.get("option");
String stringId = params.get("id");
if (option == null || stringId == null) {
return Promise.of(HttpResponse.ofCode(401));
}
int id = Integer.parseInt(stringId);
PollDao.Poll question = pollDao.find(id);
question.vote(option);
return HttpResponse.redirect302(nullToDefault(request.getHeader(REFERER), "/"));
}))
.map(POST, "/add", loadBody()
.serve(request -> {
Map<String, String> params = request.getPostParameters();
String title = params.get("title");
String message = params.get("message");
String option1 = params.get("option1");
String option2 = params.get("option2");
int id = pollDao.add(new PollDao.Poll(title, message, list(option1, option2)));
return HttpResponse.redirect302("poll/" + id);
}))
.map(POST, "/delete", loadBody()
.serve(request -> {
Map<String, String> params = request.getPostParameters();
String id = params.get("id");
if (id == null) {
return Promise.of(HttpResponse.ofCode(401));
}
pollDao.remove(Integer.parseInt(id));
return HttpResponse.redirect302("/");
}));

Also, we defined main() method which will start our launcher:

public static void main(String[] args) throws Exception {
Launcher launcher = new ApplicationLauncher();
launcher.launch(args);
}

And that's it, we have a full-functional poll application!

Running the application#

If you want to run the example, you need to from GitHub and import it as a Maven project. Check out branch v4.3. Before running the example, build the project (Ctrl + F9 for IntelliJ IDEA). Open ApplicationLauncher class and run its main() method. Then open your favourite browser and go to localhost:8080