Introduction to Jooby

1. Overview

Jooby is a scalable and fast micro web framework build on top of the most used NIO web servers. It’s very straightforward and modular, clearly designed for the modern day’s web architecture. It comes with support for Javascript and Kotlin too.

By default, Jooby comes with great support for Netty, Jetty, and Undertow.

In this article, we’ll learn about overall Jooby project structure and how to build a simple web application using Jooby.

2. Application Architecture

A simple Jooby application structure will like below:

├── public
|   └── welcome.html
├── conf
|   ├── application.conf
|   └── logback.xml
└── src
|   ├── main
|   |   └── java
|   |       └── com
|   |           └── baeldung
|   |               └── jooby
|   |                   └── App.java
|   └── test
|       └── java
|           └── com
|               └── baeldung
|                   └── jooby
|                       └── AppTest.java
├── pom.xml

Point to note here is that in public directory we can put static files like css/js/html etc. In the conf directory, we can put any configuration file that an application needs like logback.xml or application.conf etc.

3. Maven Dependency

We can create a simple Jooby application by adding the following dependency into our pom.xml:

<dependency>
    <groupId>org.jooby</groupId>
    <artifactId>jooby-netty</artifactId>
    <version>1.1.3</version>
</dependency>

If we want to choose Jetty or Undertow we can use the following dependency:

<dependency>
    <groupId>org.jooby</groupId>
    <artifactId>jooby-jetty</artifactId>
    <version>1.1.3</version>
</dependency>
<dependency>
    <groupId>org.jooby</groupId>
    <artifactId>jooby-undertow</artifactId>
    <version>1.1.3</version>
</dependency>

You can check the latest version of the Jooby project in the Central Maven Repository.

Jooby also has a dedicated Maven archetype. We can use it to create a sample project with all necessary dependencies pre-built.

We can use the following script to generate the sample project:

mvn archetype:generate -B -DgroupId=com.baeldung.jooby -DartifactId=jooby
-Dversion=1.0 -DarchetypeArtifactId=jooby-archetype
-DarchetypeGroupId=org.jooby -DarchetypeVersion=1.1.3

4. Building an Application


==== 4.1. Initiating the Server

To start the embedded server, we need to use following code snippet:

public class App extends Jooby {
    public static void main(String[] args) {
        run(App::new, args);
    }
}

Once started the server will be running on default port 8080.

We can also configure the back-end server with custom port and custom HTTPS port:

{
    port( 8081 );
    securePort( 8443 );
}

4.2. Implementing the Router

It’s very easy to create path based router in Jooby. For example, we can create a router for path ‘/login‘ in following way:

{
    get( "/login", () -> "Hello from Baeldung");
}

In a similar way, if we want to handle other HTTP methods like POST, PUT, etc we can use below code snippet:

{
    post( "/save", req -> {
        Mutant token = req.param( "token" );
        return token.intValue();
    });
}

Here, we’re fetching the request param name token from the request. By default, all request params are type-casted into Jooby‘s Mutant data type. Based on the expectation, we can convert it into any supported primitive data type.

We can check any url param in following way:

{
    get( "/user/{id}", req -> "Hello user : " + req.param("id").value() );
    get( "/user/:id", req -> "Hello user: " + req.param("id").value() );
}

We can use any of the above. It’s also possible to find params starting with a fixed content. For example, we can find a URL parameter starting with ‘uid:’ in following way:

{
    get( "/uid:{id}", req -> "Hello User with id : uid" +
        req.param("id").value());
}

4.3. Implementing MVC Pattern Controller

For an enterprise application, Jooby comes with an MVC API much like any other MVC frameworks like Spring MVC.

For example, we can handle a path called ‘/hello‘ :

@Path("/hello")
public class GetController {
    @GET
    public String hello() {
        return "Hello Baeldung";
    }
}

Very similarly we can create a handler to handle other HTTP methods with @POST, @PUT, @DELETE, etc. annotation.

4.4. Handling Static Content

To serve any static content like HTML, Javascript, CSS, image, etc., we need to place those file in the public directory.

Once placed, from the router we can map any url to these resources:

{
    assets( "/employee" , "form.html" );
}

4.5. Handling Form

Jooby’s Request interface by default handles any form object without using any manual type casting.

Let’s assume we need to submit employee details through a form. At first step, we need to create an Employee bean object that we will use to hold the data:

public class Employee {
    String id;
    String name;
    String email;

    // standard constructors, getters and setters
}

Now, we need to create a page to create the form:

<form enctype="application/x-www-form-urlencoded" action="/submitForm"
    method="post">
    <input name="id" />
    <input name="name" />
    <input name="email" />
    <input type="submit" value="Submit"/>
</form>

Next, we’ll create a post handler to address this form and fetch the submitted data:

post( "/submitForm", req -> {
    Employee employee = req.params(Employee.class);
    // ...
    return "empoyee data saved successfullly";
});

Point to note here is that we must need to declare form enctype as application/x-www-form-urlencoded to support the dynamic form binding.

By Request.file(String filename) we can retrieve the uploaded file:

post( "/upload", req -> {
    Upload upload = req.file("file");
    // ...
    upload.close();
});

4.6. Implementing a Filter

Out of the box, Jooby provides the flexibility to define global filters as well as the path-based filters.

Implementing filter in Jooby is a little bit tricky since we need to configure the URL path twice, once for the filter and once again for the handler.

For example, if we have to implement a filter for a URL path called ‘/filter’, we need to implement the filter in this path explicitly:

get( "/filter", ( req, resp, chain ) -> {
    // ...
    chain.next( req, resp );
});

The syntax is very similar to Servlet filter. It’s possible to restrict the request and send back the response in the filter itself by calling Response.send(Result result) method.

Once the filter is implemented, we need to implement the request handler:

get("/filter", (req, resp) -> {
    resp.send("filter response");
});

4.7. Session

Jooby comes with two types of session implementation; in-memory and cookie-based.

Implementing in-memory session management is quite simple. We have the options to choose any of the high-throughput session stores available with Jooby like EhCache, Guava, HazleCast, Cassandra, Couchbase, Redis, MongoDB, and Memcached.

For example, to implement a Redis-based session storage, we need to add following Maven dependency:

<dependency>
    <groupId>org.jooby</groupId>
    <artifactId>jooby-jedis</artifactId>
    <version>1.1.3</version>
</dependency>

Now we can use below code snippet to enable session management:

{
    use(new Redis());
    session(RedisSessionStore.class);

    get( "/session" , req -> {
        Session session = req.session();
        session.set("token", "value");
        return session.get("token").value();
    });
}

Point to note here is that we can configure Redis url as the ‘db’ property in the application.conf.

To enable cookie based session management, we need to declare cookieSession(). If cookie based approach is selected, we must need to declare application.secret property in the application.conf file. Since each cookie will be signed will be signed with this secret key, it’s always advisable to use long random string fragment as a secret key.*
*

In both in-memory and cookie based approach, we must have to declare the necessary configuration parameter in the application.conf file, else the application will throw an IllegalStateException at start up.

5. Testing

Testing MVC route is indeed easy since a route is bound to a strategy for some class. This makes it easy to run unit tests against all routes.

For example, we can quickly create a test-case for default URL:

public class AppTest {

    @ClassRule
    public static JoobyRule app = new JoobyRule(new App());

    @Test
    public void given_defaultUrl_expect_fixedString() {

        get("/").then().assertThat().body(equalTo("Hello World!"))
          .statusCode(200).contentType("text/html;charset=UTF-8");
    }
}

Point to note here is that using @ClassRule annotation will create only one instance of the server for all test-cases. If we need to build separate instances of the servers for every test-cases we have to use the @Rule annotation without the static modifier.

We can also use Jooby’s MockRouter to test the path in the same way:

@Test
public void given_defaultUrl_with_mockrouter_expect_fixedString()
  throws Throwable {

    String result = new MockRouter(new App()).get("/");

    assertEquals("Hello World!", result);
}

6. Conclusion

In this tutorial, we explored the Jooby project and its essential functionality.

Like always, the full source code is available over on GitHub.

Leave a Reply

Your email address will not be published.