MVC Web Application with ActiveJ
Tags: java, webdev, tutorial
Published: 2020-11-19 19:41:41

### Intro
In this tutorial we will create an asynchronous servlet that adds contacts to a list, parses requests and processes form validation with the help of [ActiveJ](https://activej.io) framework. You can find the source code of this tutorial on [GitHub](https://github.com/activej/activej/tree/v3.0/examples/tutorials/decoder).

### Setting up the project
We'll need the following dependencies:
```xml
<dependencies>
  <dependency>
    <groupId>io.activej</groupId>
    <artifactId>activej-launchers-http</artifactId>
    <version>3.0</version>
  </dependency>
  <dependency>
    <groupId>com.github.spullara.mustache.java</groupId>
    <artifactId>compiler</artifactId>
    <version>0.9.4</version>
  </dependency>
  <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
  </dependency>
</dependencies>
```
We'll use the following ActiveJ technologies:
* [ActiveInject](https://inject.activej.io) - lightweight and powerful dependency injection library with great performance and no third-party dependencies. 
* [ActiveJ HTTP](https://activej.io/http) - high-performance asynchronous HTTP clients and servers.
* [ActiveJ Launcher](https://activej.io/launcher) - takes care of full application lifecycle, service management, and logging. Perfectly compatible with ActiveInject.

### Time to code!
This tutorial represents the MVC pattern:
* To **model** a `Contact` representation, we will create a plain Java class with fields (`name`, `age`, `address`), constructor, and accessors to the fields:
```java
class Contact {
	private final String name;
	private final Integer age;
	private final Address address;

	public Contact(String name, Integer age, Address address) {
		this.name = name;
		this.age = age;
		this.address = address;
	}

	public String getName() {
		return name;
	}

	public int getAge() {
		return age;
	}

	public Address getAddress() {
		return address;
	}

	@Override
	public String toString() {
		return "Contact{name='" + name + '\'' + ", age=" + age + ", address=" + address + '}';
	}
}
```
The `Address` class is pretty simple:
```java
class Address {
	private final String title;

	public Address(String title) {
		this.title = title;
	}

	public String getTitle() {
		return title;
	}

	@Override
	public String toString() {
		return "Address{title='" + title + '\'' + '}';
	}
}

```

To simplify the tutorial, we will use an ArrayList to store the `Contact` objects. `ContactDAO` interface and its implementation are used for this purpose:
```java
interface ContactDAO {
	List<Contact> list();

	void add(Contact user);
}
```
```java
class ContactDAOImpl implements ContactDAO {
	private final List<Contact> userList = new ArrayList<>();

	@Override
	public List<Contact> list() {
		return userList;
	}

	@Override
	public void add(Contact user) {
		userList.add(user);
	}
}
```
* To build a **view** we will use a single HTML file, compiled with the help of the Mustache template engine:
```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Decoder example</title>
  <style>
    table {
      font-family: arial, sans-serif;
      border-collapse: collapse;
      width: 100%;
    }

    td, th {
      border: 1px solid #dddddd;
      text-align: left;
      padding: 8px;
    }

    tr:nth-child(even) {
      background-color: #dddddd;
    }

    div {
      text-align: center;
    }

    span {
      color: red;
    }

  </style>
</head>
<body>
<div>
  <form action="/add" method="post">
    <table>
      <tr>
        <th>Name: <input type="text" name="name"> <span>{{errors.name}}</span></th>
        <th>Age: <input type="text" name="age"> <span>{{errors.age}}</span></th>
        <th>Address: <input type="text" name="title"> <span>{{errors.contact-address-title}}</span>
        </th>
      </tr>
    </table>
    <input type="submit" value="Submit">
  </form>
</div>
<br>
<table>
  <tr>
    <th>Name</th>
    <th>Age</th>
    <th>Address</th>
  </tr>
  {{#contacts}}
  <tr>
    <th>{{name}}</th>
    <th>{{age}}</th>
    <th>{{address.title}}</th>
  </tr>
  {{/contacts}}
</table>
</body>
</html>
```

* An [`AsyncServlet`](https://github.com/activej/activej/blob/v3.0/core-http/src/main/java/io/activej/http/AsyncServlet.java) will be used as a **controller**. We will also add [`RoutingServlet`](https://github.com/activej/activej/blob/v3.0/core-http/src/main/java/io/activej/http/RoutingServlet.java) for routing requests to the needed endpoints. We'll get to this in a moment.

Let’s create `HttpDecoderExample` class which extends [`HttpServerLauncher`](https://github.com/activej/activej/blob/v3.0/launchers/http/src/main/java/io/activej/launchers/http/HttpServerLauncher.java). By extending `HttpServerLauncher` we will take care of the server’s lifecycle and service management. Next, we provide two custom parsers based on [`Decoder`](https://github.com/activej/activej/blob/v3.0/core-http/src/main/java/io/activej/http/decoder/Decoder.java)- `ADDRESS_DECODER` and `CONTACT_DECODER` - which will be used for validation. `Decoder` class provides you with tools for parsing requests.

```java
public final class HttpDecoderExample extends HttpServerLauncher {
	private static final String SEPARATOR = "-";

	private static final Decoder<Address> ADDRESS_DECODER = Decoder.of(Address::new,
			ofPost("title", "")
					.validate(param -> !param.isEmpty(), "Title cannot be empty")
	);

	private static final Decoder<Contact> CONTACT_DECODER = Decoder.of(Contact::new,
			ofPost("name")
					.validate(name -> !name.isEmpty(), "Name cannot be empty"),
			ofPost("age")
					.map(Integer::valueOf, "Cannot parse age")
					.validate(age -> age >= 18, "Age must not be less than 18"),
			ADDRESS_DECODER.withId("contact-address")
	);
```

Also, we need to create `applyTemplate(Mustache mustache, Map<String, Object> scopes)` method to fill the provided Mustache template with the given data:

```java
private static ByteBuf applyTemplate(Mustache mustache, Map<String, Object> scopes) {
	ByteBufWriter writer = new ByteBufWriter();
	mustache.execute(writer, scopes);
	return writer.getBuf();
}
```
Next, let’s provide a `ContactDAOImpl` factory method:

```java
@Provides
ContactDAO dao() {
	return new ContactDAOImpl();
}
```

Now we have everything needed to create the controller `AsyncServlet` to handle requests:

```java
@Provides
AsyncServlet mainServlet(ContactDAO contactDAO) {
	Mustache contactListView = new DefaultMustacheFactory().compile("static/contactList.html");
	return RoutingServlet.create()
			.map("/", request ->
					HttpResponse.ok200()
							.withBody(applyTemplate(contactListView, map("contacts", contactDAO.list()))))
			.map(POST, "/add", AsyncServletDecorator.loadBody()
					.serve(request -> {
						Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request);

						if (decodedUser.isLeft()) {
							contactDAO.add(decodedUser.getLeft());
						}
						Map<String, Object> scopes = map("contacts", contactDAO.list());
						if (decodedUser.isRight()) {
							scopes.put("errors", decodedUser.getRight().toMap(SEPARATOR));
						}
						return HttpResponse.ok200()
								.withBody(applyTemplate(contactListView, scopes));
					}));
}
```

Here we provide an `AsyncServlet`, which receives [`HttpRequest`](https://github.com/activej/activej/blob/v3.0/core-http/src/main/java/io/activej/http/HttpRequest.java)s from clients, creates [`HttpResponse`](https://github.com/activej/activej/blob/v3.0/core-http/src/main/java/io/activej/http/HttpResponse.java)s depending on the route path and sends it.

Inside the `RoutingServlet` two route paths are defined. The first one matches requests to the root route `/` - it simply displays a contact list. The second one, `/add` - is an HTTP POST method that adds or declines adding new users. We will process this request parsing with the help of the aforementioned `Decoder` by using `decode` method:
```java
Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request);
```
`Either` represents a value of two possible data types which are either `Left`(`Contact`) or `Right`(`DecodeErrors`). In order to determine whether a parse was successful or not, we check it’s value by using the `isLeft()` and `isRight()` methods.
Finally, write down the `main` method which will launch our application:

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

### Time to test!
You've just created and launched an MVC web application with asynchronous and high-performance server! Now you can test the app by visiting [localhost:8080](http://localhost:8080).