In this example you can learn how to implement template engines in DataKernel applications by following the example of creating a simple Poll app. This app creates new polls with a custom title, description and options. Creating a poll generates its unique link that leads to a page where you can vote.
See how simple it is to implement such features using DataKernel’s HTTP module: 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.
The ApplicationLauncher launches our application, takes care of routing and proper content generation on HTML pages. We will extend DataKernel’s HttpServerLauncher to manage the application’s lifecycle:
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:
Next, we provide AsyncServlet:
@Provides
AsyncServlet servlet(PollDao pollDao) {
Mustache singlePollView = new DefaultMustacheFactory().compile("templates/singlePollView.html");
Mustache singlePollCreate = new DefaultMustacheFactory().compile("templates/singlePollCreate.html");
Mustache listPolls = new DefaultMustacheFactory().compile("templates/listPolls.html");
return RoutingServlet.create()
.map(GET, "/", request -> HttpResponse.ok200()
.withBody(applyTemplate(listPolls, map("polls", pollDao.findAll().entrySet()))))
}
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. In the example above we’ve added the request to the homepage by using the map method.
Method map(@Nullable HttpMethod method, String path, AsyncServlet servlet) adds a route to the RoutingServlet:
GET
, POST
and so on)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-functioning poll application!
If you want to run the example, you need to clone DataKernel and import it as a Maven project. Check out branch v3.1. 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.