The functional programming model

The functional programming model uses the API that has functional interfaces such as RouterFunction and HandlerFunction. It uses Java 8 Lambda style programming with routing and request handling instead of the Spring MVC annotations. They are simple, but powerful, building blocks for creating web applications.

The following is an example of functional request handling:

    package com.packt.patterninspring.chapter11.web.reactive.function; 
 
    import static org.springframework.http.MediaType.APPLICATION_JSON; 
    import static org.springframework.web.reactive.
function.BodyInserters.fromObject; import org.springframework.web.reactive.
function.server.ServerRequest; import org.springframework.web.reactive.
function.server.ServerResponse; import com.packt.patterninspring.chapter11.
web.reactive.model.Account; import com.packt.patterninspring.chapter11.
web.reactive.repository.AccountRepository; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public class AccountHandler { private final AccountRepository repository; public AccountHandler(AccountRepository repository) { this.repository = repository; } public Mono<ServerResponse> findById(ServerRequest request) { Long accountId = Long.valueOf(request.pathVariable("id")); Mono<ServerResponse> notFound =
ServerResponse.notFound().build(); Mono<Account> accountMono =
this.repository.findById(accountId); return accountMono .flatMap(account -> ServerResponse.ok().contentType
(APPLICATION_JSON).body(
fromObject(account))) .switchIfEmpty(notFound); } public Mono<ServerResponse> findAll(ServerRequest request) { Flux<Account> accounts = this.repository.findAll(); return ServerResponse.ok().contentType
(APPLICATION_JSON).body(accounts,
Account.class); } public Mono<ServerResponse> create(ServerRequest request) { Mono<Account> account = request.bodyToMono(Account.class); return ServerResponse.ok().build(this.
repository.save(account)); } }

In the preceding code, the class file, AccountHandler.java, is based on the functional reactive programming model. Here, I have used the reactor framework to handle the request. Two functional interfaces, ServerRequest and ServerResponse, are used to handle requests and to generate responses.

Let's see the Repositories classes of this application. The following AccountRepository and AccountRepositoryImpl classes are the same for both type of applications-Annotation-based and the functional-based programming model.

Let's create an interface AccountRepository.java class as follows:

    package com.packt.patterninspring.chapter11.
reactivewebapp.repository; import com.packt.patterninspring.chapter11.
reactivewebapp.model.Account; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; public interface AccountRepository { Mono<Account> findById(Long id); Flux<Account> findAll(); Mono<Void> save(Mono<Account> account); }

The preceding code is an interface, let's implements this interface with the AccountRepositoryImpl.java class as following:

    package com.packt.patterninspring.chapter11.
web.reactive.repository; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.stereotype.Repository; import com.packt.patterninspring.chapter11.web.
reactive.model.Account; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Repository public class AccountRepositoryImpl implements AccountRepository { private final Map<Long, Account> accountMap = new
ConcurrentHashMap<>(); public AccountRepositoryImpl() { this.accountMap.put(1000l, new Account(1000l,
"Dinesh Rajput", 50000l,
"Sector-1")); this.accountMap.put(2000l, new Account(2000l,
"Anamika Rajput", 60000l,
"Sector-2")); this.accountMap.put(3000l, new Account(3000l,
"Arnav Rajput", 70000l,
"Sector-3")); this.accountMap.put(4000l, new Account(4000l,
"Adesh Rajput", 80000l,
"Sector-4")); } @Override public Mono<Account> findById(Long id) { return Mono.justOrEmpty(this.accountMap.get(id)); } @Override public Flux<Account> findAll() { return Flux.fromIterable(this.accountMap.values()); } @Override public Mono<Void> save(Mono<Account> account) { return account.doOnNext(a -> { accountMap.put(a.getId(), a); System.out.format("Saved %s with id %d%n", a, a.getId()); }).thenEmpty(Mono.empty()); // return accountMono; } }

As you can see in the preceding code, we created the AccountRepository class. This class has only three methods: findById(), findAll(), and save(). We implemented these methods according to the business requirements. In this repository class, I have, especially, used the Flux and Mono react types to make it a reactive-based application.

Let's create the server for the functional-based programming model. In Annotation-based programming, we use the simple tomcat container to deploy the web application. But for this functional-based programming, we have to create a Server class to start the Tomcat server or Reactor server, as follows:

    package com.packt.patterninspring.chapter11.web.reactive.function; 
 
    //Imports here 
 
    public class Server { 
 
      public static final String HOST = "localhost"; 
  
      public static final int TOMCAT_PORT = 8080; 
      public static final int REACTOR_PORT = 8181; 
    
      //main method here, download code for GITHUB 
    
      public RouterFunction<ServerResponse> routingFunction() { 
         AccountRepository repository = new AccountRepositoryImpl(); 
         AccountHandler handler = new AccountHandler(repository); 
 
         return nest(path("/account"), nest(accept(APPLICATION_JSON), 
           route(GET("/{id}"), handler::findById) 
           .andRoute(method(HttpMethod.GET), handler::findAll) 
           ).andRoute(POST("/").and(contentType
(APPLICATION_JSON)), handler::create)); } public void startReactorServer() throws InterruptedException { RouterFunction<ServerResponse> route = routingFunction(); HttpHandler httpHandler = toHttpHandler(route); ReactorHttpHandlerAdapter adapter = new
ReactorHttpHandlerAdapter(httpHandler); HttpServer server = HttpServer.create(HOST, REACTOR_PORT); server.newHandler(adapter).block(); } public void startTomcatServer() throws LifecycleException { RouterFunction<?> route = routingFunction(); HttpHandler httpHandler = toHttpHandler(route); Tomcat tomcatServer = new Tomcat(); tomcatServer.setHostname(HOST); tomcatServer.setPort(TOMCAT_PORT); Context rootContext = tomcatServer.addContext("",
System.getProperty("java.io.tmpdir")); ServletHttpHandlerAdapter servlet = new
ServletHttpHandlerAdapter(httpHandler); Tomcat.addServlet(rootContext, "httpHandlerServlet", servlet); rootContext.addServletMapping("/", "httpHandlerServlet"); tomcatServer.start(); } }

As you can see in the preceding Server.java class file, I have added both, the Tomcat and Reactor servers. The Tomcat server uses port 8080, but the Reactor server uses the port 8181.

This Server.java class has three methods. The first method, routingFunction(), is responsible for handling client requests by using the AccountHandler class. It depends on the AccountRepository class. The second method, startReactorServer(), is responsible for starting the Reactor server by using the ReactorHttpHandlerAdapter class of the reactor server. This class takes an object of the HttpHandler class as a constructor argument to create the request handler mapping. Similarly, the third method, startTomcatServer(), is responsible for starting the Tomcat server. And it is bound to the HttpHandler object through a reactor adapter class, ServletHttpHandlerAdapter.

You can run this server class file as a Java application, and see the output on the browser by typing the URL, http://localhost:8080/account/:

You can also type the same URL with port 8181 for the Reactor server, as follows, and you will get the same output:

http://localhost:8181/account/

In this section, you learned how to create a reactive web application using the Spring-web-reactive module. We created the web application by using both the programming paradigms: Annotation-based and Functional-based.

In the next section, we'll discuss client-side code, and how a client accesses the reactive web application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset