1. Overview

Jooby is a scalable and fast microweb framework built on top of the most used NIO web servers. It’s very straightforward and modular, clearly designed for 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 the overall Jooby project structure and how to build a simple web application using Jooby.

2. Application Architecture

A simple Jooby application structure will be like the 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

The point to note here is that in the 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 to our pom.xml:

<dependency>
    <groupId>io.jooby</groupId>
    <artifactId>jooby-netty</artifactId>
    <version>2.16.1</version>
</dependency>

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

<dependency>
    <groupId>io.jooby</groupId>
    <artifactId>jooby-jetty</artifactId>
    <version>2.16.1</version>
</dependency>
<dependency>
    <groupId>io.jooby</groupId>
    <artifactId>jooby-undertow</artifactId>
    <version>2.16.1</version>
</dependency>

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

4. Building an Application

4.1. Initiating the Server

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

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

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

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

{
    setServerOptions(new ServerOptions().
        .setPort(8080)
        .setSecurePort(8433));
}

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 the following way:

{
    get( "/login", ctx -> "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", ctx -> {
        String userId = ctx.query("id").value();
        return userId;
    });
}

Here, we’re fetching the request param name id from the request. There are several parameter types: header, cookie, path, query, form, multipart, session, and flash. All of them share a unified/type-safe API for accessing and manipulating their values.

We can check any URI path param in the following way:

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

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

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

4.3. Implementing MVC Pattern Controller

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

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

@Path("/submit")
public class PostController {
    
    @POST
    public String hello() {
        return "Submit 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, images, etc., we need to place those files in the public directory.

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

{
    assets("/employee", "public/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. In the 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">
    <label>Employed id:</label><br>
    <input name="id"/><br>
    <label>Name:</label><br>
    <input name="name"/><br>
    <label>Email:</label><br>
    <input name="email"/><br><br>
    <input type="submit" value="Submit"/>
</form>

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

post("/submitForm", ctx -> {
    Employee employee = ctx.path(Employee.class);
    // ...
    return "employee data saved successfully";
});

The 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.

4.6. Session

Jooby comes with three types of session implementation; in-memory, signed session, and in-memory with stores.

In-Memory Session

Implementing in-memory session management is quite simple. In this jooby stores the session data in memory and used a cookie/header to read/save the session ID.

{
    get("/sessionInMemory", ctx -> {
        Session session = ctx.session();
        session.put("token", "value");
        return session.get("token").value();
    });
}

Signed Session

The signed session is a stateless session store that expects to find a session token in each request. In this case, the session doesn’t keep any state.

Let’s take an example :

{
    String secret = "super secret token";

    setSessionStore(SessionStore.signed(secret));

    get("/signedSession", ctx -> {
        Session session = ctx.session();
        session.put("token", "value");
        return session.get("token").value();
    });
}

In the above example, we have used the secret as a key to sign the data and created a cookie session store using the secret.

Store

In addition to built-in memory stores Jooby also provides below :

  • Caffeine: In-memory session store using Caffeine cache.
  • JWT: JSON Web Token session store.

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

<dependency>
    <groupId>io.jooby</groupId>
    <artifactId>jooby-redis</artifactId>
    <version>2.16.1</version>
</dependency>

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

{
    install(new RedisModule("redis"));
    setSessionStore(new RedisSessionStore(require(RedisClient.class)));
    get("/redisSession", ctx -> {
        Session session = ctx.session();
        session.put("token", "value");
        return session.get("token");
    });
}

The point to note here is that we can configure the Redis url as the ‘db’ property in the application. conf  by adding the below line:

redis = "redis://localhost:6379"

For more examples please refer Jooby documentation

5. Testing

Testing the MVC route is indeed easy since a route is bound to a strategy for some classes. The MockRouter lets you execute the route function.

before testing we need to add the Jooby test dependency:

<dependency>
  <groupId>io.jooby</groupId>
  <artifactId>jooby-test</artifactId>
  <version>2.16.1</version>
</dependency>

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

public class AppUnitTest {

    @Test
    public void given_defaultUrl_with_mockrouter_expect_fixedString() {
        MockRouter router = new MockRouter(new App());
        assertEquals("Hello World!", router.get("/")
            .value());
    }
}

In the above example, MockRouter returns the value produced by the route handler.

Integration testing is supported in Jooby via JUnit 5.

Let’s check the below example:

@JoobyTest(value = App.class, port = 8080)
public class AppLiveTest {

    static OkHttpClient client = new OkHttpClient();

    @Test
    public void given_defaultUrl_expect_fixedString() {
        Request request = new Request.Builder().url("http://localhost:8080").build();
        
        try (Response response = client.newCall(request).execute()) {
            
            assertEquals("Hello World!", response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

The @JoobyTest annotation takes care of starting and stopping the application. By default, the port is: 8911.

You can change it by using the port() method:

@JoobyTest(value = App.class, port = 8080)

In the above example, we have added the @JoobyTest annotation at the class level. We can also use this annotation at the method level. For more examples please check the Jooby official documentation.

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.


« 上一篇: Kryo简介