User class with Reactive – REST
In the first chapter, we introduced Ticket and User, two classes involved with our web service. As the Ticket class is a little complex compared to the User class, we will use the User class to understand Reactive components.
As Reactive in Spring 5 is not fully stable yet, we are going to talk about Reactive in only a few chapters. So we will create a separate package for Reactive-based REST APIs. Also, we will add Reactive-based dependencies in our existing pom.xml file.
First, we will have to add all Reactive dependencies. Here, we will add the code in our existing pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.packtpub.restapp</groupId>
<artifactId>ticket-management</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ticket-management</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-bom</artifactId>
<version>Bismuth-RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.5.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>1.5.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>1.5.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.ipc</groupId>
<artifactId>reactor-netty</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
In the preceding POM configuration, we have added Reactor dependencies on top of our existing dependencies (mentioned as follows):
- reactive-streams
- reactor-core
- reactor-netty
- tomcat-embed-core
- spring-webflux
These are the libraries needed in order to work with Reactors.
The User class components are as follows:
- userid
- username
- user_email
- user_type (admin, general user, CSR)
Here, we have four variables used for the User class. To make it simpler to understand Reactive components, we use only two variables (userid, username). Let's create a POJO class with only userid and username.
The User POJO class is as follows:
package com.packtpub.reactive;
public class User {
private Integer userid;
private String username;
public User(Integer userid, String username){
this.userid = userid;
this.username = username;
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
In the preceding class, I have used two variables and a constructor to fill the variables while instantiating. Also, getters/setters are used to access those variables.
Let's create a Reactive repository for the User class:
package com.packtpub.reactive;
import reactor.core.publisher.Flux;
public interface UserRepository {
Flux<User> getAllUsers();
}
In the preceding code, we have introduced a Reactive repository for User and a class with only one method, called getAllUsers. By using this method, we should be able to retrieve a list of users. Let's not talk about Flux now, as it will be discussed later.
You can see that this UserRepository is an interface. We need to have a concrete class to implement this interface in order to use this repository. Let's create a concrete class for this Reactive repository:
package com.packtpub.reactive;
import java.util.HashMap;
import java.util.Map;
import reactor.core.publisher.Flux;
public class UserRepositorySample implements UserRepository {
// initiate Users
private Map<Integer, User> users = null;
// fill dummy values for testing
public UserRepositorySample() {
// Java 9 Immutable map used
users = Map.of(
1, (new User(1, "David")),
2, (new User(2, "John")),
3, (new User(3, "Kevin"))
);
}
// this method will return all users
@Override
public Flux<User> getAllUsers() {
return Flux.fromIterable(this.users.values());
}
}
In next chapter, we will use regular map as we need to edit them in CRUD operations.
At the moment, we are able to get a list of users from the concrete class. Right now we need a web handler to retrieve the users in the controller. Let's create a handler now:
package com.packtpub.reactive;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class UserHandler {
private final UserRepository userRepository;
public UserHandler(UserRepository userRepository){
this.userRepository = userRepository;
}
public Mono<ServerResponse> getAllUsers(ServerRequest request){
Flux<User> users = this.userRepository.getAllUsers();
return ServerResponse.ok().contentType(APPLICATION_JSON).body(users, User.class);
}
}
Finally, we will have to create a server where we can keep REST APIs. In the following code, our Server class will create one REST API to get users:
package com.packtpub.reactive;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RequestPredicates.contentType;
import static org.springframework.web.reactive.function.server.RequestPredicates.method;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
import java.io.IOException;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.ipc.netty.http.server.HttpServer;
public class Server {
public static final String HOST = "localhost";
public static final int PORT = 8081;
public static void main(String[] args) throws InterruptedException, IOException{
Server server = new Server();
server.startReactorServer();
System.out.println("Press ENTER to exit.");
System.in.read();
}
public void startReactorServer() throws InterruptedException {
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create(HOST, PORT);
server.newHandler(adapter).block();
}
public RouterFunction<ServerResponse> routingFunction() {
UserRepository repository = new UserRepositorySample();
UserHandler handler = new UserHandler(repository);
return nest (
path("/user"),
nest(
accept(APPLICATION_JSON),
route(GET("/{id}"), handler::getAllUsers)
.andRoute(method(HttpMethod.GET), handler::getAllUsers)
).andRoute(POST("/").and(contentType(APPLICATION_JSON)), handler::getAllUsers));
}
}
We will discuss more about how we did this in upcoming chapters. Just make sure that you are able to understand that the code is working and you can see the output on the browser by accessing the API.
Run the Server.class and you will see the log:
Press ENTER to exit.
Now you can access the API in a browser/SoapUI/Postman, or any other client:
http://localhost:8081/user/
As we have used the 8081 port for the Reactive server, we will only have access to 8081 instead of 8080:
[
{
"userid": 100,
"username": "David"
},
{
"userid": 101,
"username": "John"
},
{
"userid": 102,
"username": "Kevin"
},
]