Custom Routes in Javalin

16 Nov 2022

Update - Thanks to the creator of Javalin for pointing out a cleaner approach we can use, in Javalin 5. See Custom Routes in Javalin - A Better Way.


I wanted to set up a "maintenance mode" for a web application built on Javalin. That is, when the site is in maintenance mode, all normal routes are ignored (and the user sees a single "in maintenance" page), unless that user logs in as a site administrator.

I very much like using the Javalin framework - but I was stuck on how to implement this feature.

There are probably other ways to do this, but here is my approach, which is simple. The only part that gives me pause is that I am using an error handling mechanism for something which is not actually an error condition.

Here are the pieces:

Javalin provides a before handler which runs before each request us processed:

Java
1
2
3
4
5
6
7
app.routes(() -> {

    before(BEFORE_HANDLER);

    // HTTP action routes for GET, POST, etc.

});

My specific BEFORE_HANDLER code checks to see if the site is in maintenance mode. If it is, it “throws” a custom error - which, in my case, is simply a way to allow Javalin to execute the “in maintenance” route - and then stop processing. Without this error handling step, Javalin would carry on processing the request.

Java
1
2
3
4
5
6
7
8
9
private static final Handler BEFORE_HANDLER = ctx -> {

    // check initial conditions

    if (someConditionMet) {
        throw new MyCustomResponse(200);
    }

};

The MyCustomResponse class is nothing more than a placeholder class which extends Javalin’s HttpResponseException. This is normally used for more typical exception handling in Javalin.

But here, we are using it to handle our custom “maintenance mode” logic:

Java
1
2
3
4
5
6
7
private static class MyCustomResponse extends HttpResponseException {

    public MyCustomResponse(int status) {
        super(status);
    }

}

Now, we can use this class in our own app.exception() handler - and this handler can contain a response - for example, my ctx.render(...) response:

Java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
app.routes(() -> {

    before(BEFORE_HANDLER);

    // HTTP action routes for GET, POST, etc.

    app.exception(MyCustomResponse.class, (e, ctx) -> {
        Map<String, Object> model = new HashMap<>();
        model.put(...);
        ctx.render("my_special_resonse_page.html", model);
    });

});

After that, the remainder of my “maintenance mode” logic simply checks if the user is already logged in as an admin, (or if the user has requested the login page). That logic is not shown here.