How to do it...

Let us create our first synchronous, asynchronous and reactive microservices by following these steps:

  1. Using Eclipse STS, create a Maven project for a Spring Boot application named ch10-deptservice that will represent a microservice for the department domain. Then, create a POM configuration which includes all the needed Spring Boot 2.0.0.M2 starter POM libraries, such as WebFlux, Spring Context, JDBC, HikariCP connection pool, Ehcache, JPA, embedded reactive Tomcat container, Reactor Netty container, FreeMarker and Thymeleaf. Also include some required support libraries, such as MySQL 5.x connector and Rx Java 2.0:
<project xmlns="..."> 
  <modelVersion>4.0.0</modelVersion> 
  .... 
  <packaging>war</packaging> 
  <parent> 
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-parent</artifactId> 
    <version>2.0.0.M2</version> 
    <relativePath/> 
  </parent> 
  <properties> 
     <project.build.sourceEncoding> 
UTF-8</project.build.sourceEncoding> 
     <project.reporting.outputEncoding> 
UTF-8</project.reporting.outputEncoding> 
     <java.version>1.8</java.version> 
     <startClass>org.packt.spring.boot.HRDeptBootApplication 
</startClass> 
  </properties> 
  <dependencies> 
    <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-jdbc</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-webflux</artifactId> 
    </dependency> 
    <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-tomcat</artifactId> 
    </dependency> 
        ... 
   <dependency> 
    <groupId>mysql</groupId> 
    <artifactId>mysql-connector-java</artifactId> 
    <version>5.1.40</version> 
    </dependency> 
    <dependency> 
    <groupId>io.reactivex.rxjava2</groupId> 
    <artifactId>rxjava</artifactId> 
    <version>2.1.0</version> 
    </dependency> 
  </dependencies> 
    .... 
  <build> 
    <plugins> 
        <plugin> 
             <groupId>org.springframework.boot</groupId> 
             <artifactId>spring-boot-maven-plugin</artifactId> 
        </plugin> 
    </plugins> 
  <finalName>ch10-dept</finalName> 
  </build> 
</project> 
  1. Create the core package, org.packt.microservice.core, and drop a bootstrap class inside it named as HRDeptBootApplication. Update the <startClass> property of pom.xml:
@SpringBootApplication 
public class HRDeptBootApplication extends 
                      SpringBootServletInitializer  { 
     
    @Override 
      protected SpringApplicationBuilder 
    configure(SpringApplicationBuilder application) { 
          return application.sources( 
HRDeptBootApplication.class); 
      } 
     
      public static void main(String[] args) throws Exception { 
        SpringApplication.run(HRDeptBootApplication.class, args); 
      } 
}
  1. Create another package, org.packt.microservice.core.config, for the reactive ApplicationContext configuration details. Drop the following @Configuration classes inside this package:
@Configuration 
@EnableCaching 
public class CachingConfig { // empty } 
 
@Configuration 
@EnableJpaRepositories( 
basePackages="org.packt.microservice.core.dao") 
@Import(RepositoryRestMvcConfiguration.class) 
@EnableTransactionManagement 
public class SpringDataConfig { // empty  } 
 
@EnableAsync 
@Configuration 
public class SpringAsynchConfig implements AsyncConfigurer { 
   
  private static Logger logger = 
    LoggerFactory.getLogger(SpringAsynchConfig.class); 
   
         @Bean("mvcTaskexecutor") 
      @Override 
          public Executor getAsyncExecutor() { 
        ConcurrentTaskExecutor executor = 
new ConcurrentTaskExecutor( 
                      Executors.newFixedThreadPool(100)); 
     
        executor.setTaskDecorator(new TaskDecorator() { 
                @Override 
                public Runnable decorate (Runnable runnable) { 
                    return () -> { 
                        long t = System.currentTimeMillis(); 
                        runnable.run(); 
                        logger.info("creating 
     ConcurrentTaskExecutor ...."); 
                        System.out.printf("Thread %s has a 
                  processing time:  %s%n", 
                   Thread.currentThread().getName(), 
                               (System.currentTimeMillis() - t)); 
                    }; 
                } 
            }); 
        return executor; 
    } 
} 
 
@Configuration 
@EnableWebFlux 
public class HttpServerConfig { 
   
  @Bean 
  public NettyContext nettyContext(ApplicationContext context) { 
    HttpHandler handler = 
      DispatcherHandler.toHttpHandler(context); 
    ReactorHttpHandlerAdapter adapter = 
new ReactorHttpHandlerAdapter(handler); 
    HttpServer httpServer = HttpServer.create("localhost", 
        Integer.valueOf("9003")); 
    return httpServer.newHandler(adapter).block(); 
} 
} 
All of these @Configuration are just the same as the previous chapters.
  1. Copy the ehcache.xml and logback.xml configuration files of the previous chapter and drop them to this project's src/main/resources folder. Update the new Ehcache's diskStore and Logger's log file path for this project.
  2. Also in src/main/resources, create the required application.properties for the auto-configuration of the embedded reactive Tomcat, JDBC and HikariCP data source setup with JPA and Hibernate 5 data persistency configuration:
server.port=8090 
server.servlet.context-path=/ch10-dept 
 
spring.datasource.driverClassName=com.mysql.jdbc.Driver 
spring.datasource.url=jdbc:mysql://localhost:3306/hrs?autoReconnect=true&useSSL=true&serverSslCert=classpath:config/spring5packt.crt 
spring.datasource.username=root 
spring.datasource.password=spring5mysql 
spring.datasource.hikari.connection-timeout=60000 
 
spring.jpa.hibernate.use-new-id-generator-mappings=false
  1. Building the proper RESTful services starts with the correct JPA entities. Copy and drop the Department entity model used in ch09 to a new package org.packt.microservice.core.model.data.
  1. Together with the entity models, create an auxiliary POJO that will contain aggregate and simple data value such as the total number of departments which is a Long object type. The main reason behind this is that JSON and XML marshallers do not directly convert wrapper objects (for example, Integer, Double, Float) into JSON objects or XML entities:
public class CountDept implements Serializable{ 
  private Long count; 
 
  public Long getCount() { 
    return count; 
  } 
 
  public void setCount(Long count) { 
    this.count = count; 
  } 
} 
  1. Then, implement the @Repository class for DepartmentDao using Spring Data JPA. Apply Ehcache caching to each data retrieval operation. Drop this class inside org.packt.microservice.core.dao package:
@Repository 
public interface DepartmentRepository extends 
        JpaRepository<Department, Integer>{ 
 
  @Cacheable("departmentCache") 
  public List<Department> findByName(String name); 
   
  @Cacheable("departmentCache") 
  public List<Department> findByDeptid(Integer deptId); 
} 
  1. Next, create a DepartmentService, that will compose the RESTful services to be exposed by the microservice. The template includes both asynchronous and synchronous service signatures:
public interface DepartmentService { 
  public Department findDeptByid(Integer id); 
   public List<Department> findAllDepts(); 
   public List<Department> findDeptsByName(String name); 
   public List<Department> findDeptsByDeptId(Integer deptid); 
   public void saveDeptRec(Department dept); 
 
   public CompletableFuture<List<Department>> readDepartments(); 
   public Future<Department>  readDepartment(Integer id); 
}
  1. Implement the service class using the preceding DepartmentRepository class. The implementations include blocking transactions and non-blocking ones that return Future and CompletableFuture tasks. And, since the @EnableTransactionManagement has been invoked by the configuration class SpringDataConfig, the @Transactional annotation must be applied per service implementation class for JPA persistency and commit/rollback management:
@Service 
@Transactional 
public class DepartmentServiceImpl implements DepartmentService{ 
   
  @Autowired 
  private DepartmentRepository departmentRepository; 
 
  @Override 
  public List<Department> findAllDepts() { 
    return departmentRepository.findAll(); 
  } 
 
  @Override 
  public List<Department> findDeptsByName(String name) { 
    return departmentRepository.findByName(name); 
  } 
 
  // refer to sources 
   
  @Override 
  public CompletableFuture<List<Department>> readDepartments() { 
    return CompletableFuture.completedFuture( 
departmentRepository.findAll()); 
  } 
   
  @Async 
  public Future<Department> readDepartment(Integer id) { 
    return new AsyncResult<>(departmentRepository.findById(id) 
.orElse(new Department())); 
  } 
}
  1. Spring 5's RouterFunction<?> has a separate set of services, which are written as a handler class. A handler class contains all the HandlerFunction<?> implementation that can be mapped later with the corresponding URL. Below is a handler class for this microservice that must be placed inside the org.packt.microservice.core.handler package:
@Component 
public class DeptDataHandler { 
   
   // refer to sources 
   
   public Mono<ServerResponse> deptList(ServerRequest req) { 
    Flux<Department> flux = Flux.fromIterable( 
departmentServiceImpl.findAllDepts()); 
    return ok().contentType(MediaType.APPLICATION_STREAM_JSON) 
.body(flux, Department.class); 
  } 
 
   public Mono<ServerResponse> chooseDeptById(ServerRequest req) { 
    Scheduler subWorker = Schedulers.newSingle("sub-thread"); 
    Mono<Department> emp = Mono.defer(() -> Mono.justOrEmpty( 
departmentServiceImpl.findDeptByid( 
Integer.parseInt(req.pathVariable("id"))))) 
.subscribeOn(subWorker); 
    return ok().contentType(MediaType.APPLICATION_STREAM_JSON) 
.body(emp, Department.class) 
        .switchIfEmpty(ServerResponse.notFound().build()); 
  } 
 
  public Mono<ServerResponse> chooseFluxDepts(ServerRequest req) { 
    return ok().contentType(MediaType.APPLICATION_STREAM_JSON) 
.body(req.bodyToFlux(Integer.class) 
.flatMap((id) -> Mono.justOrEmpty( 
departmentServiceImpl.findDeptByid(id))), 
    Department.class) 
        .switchIfEmpty(ServerResponse.notFound().build()); 
  } 
 
  public Mono<ServerResponse> saveDepartmentMono( 
ServerRequest req) { 
      Scheduler subWorker = Schedulers.newSingle("sub-thread"); 
      Mono<Department> department = req.bodyToMono( 
Department.class).doOnNext( 
departmentServiceImpl::saveDeptRec) 
.subscribeOn(subWorker); 
         return ok().contentType( 
MediaType.APPLICATION_STREAM_JSON) 
.build(department.then()); 
  } 
 
  public Mono<ServerResponse> countDepts(ServerRequest req) { 
    Mono<Long> count = Flux.fromIterable(departmentServiceImpl 
.findAllDepts())  .count();   
    CountDept countDept = new CountDept(); 
    countDept.setCount(count.block()); 
    Mono<CountDept> monoCntDept = Mono.justOrEmpty(countDept); 
    return ok().contentType( 
MediaType.APPLICATION_STREAM_JSON) 
.body(monoCntDept, CountDept.class) 
        .switchIfEmpty(ServerResponse.notFound().build()); 
  } 
} 
  1. At this point, synchronous, asynchronous, and reactive RESTful services can now be implemented to build this microservice for the department domain. Let us start by implementing the GET and POST synchronous REST web services using the @RestController, @GetMapping, and @PostMapping of Spring 5:
@RestController 
public class DeptBlockingController { 
   
  @Autowired 
  private DepartmentService departmentServiceImpl; 
   
  @GetMapping(value="/selectDept/{id}", 
produces= MediaType.APPLICATION_JSON_VALUE) 
  public Department blockDepartment( 
@PathVariable("id") Integer id) { 
    return departmentServiceImpl.findDeptByid(id); 
  } 
     
  @GetMapping(value="/listDept", 
produces= MediaType.APPLICATION_JSON_VALUE) 
  public List<Department> blockListDept() { 
    return departmentServiceImpl.findAllDepts(); 
  } 
   
  @PostMapping(value="/saveDeptRec", 
consumes= MediaType.APPLICATION_JSON_VALUE) 
  public Boolean blockSaveDept(@RequestBody Department dept) { 
    try{ 
      departmentServiceImpl.saveDeptRec(dept); 
      return true; 
    }catch(Exception e){ 
      return false; 
    } 
  } 
}
  1. Now, build the asynchronous REST services that use WebAsyncTask, Callable, and DeferredResult tasks that return the Future<T> and CompletableFuture<T> objects:
@RestController 
public class DeptAsyncController { 
   
  @Autowired 
  private DepartmentService departmentServiceImpl; 
   
  @GetMapping(value="/webSyncDeptList.json", 
produces ="application/json", 
headers = {"Accept=text/xml, application/json"}) 
  public WebAsyncTask<List<Department>> websyncDeptList(){ 
      Callable<List<Department>> callable = 
new Callable<List<Department>>() { 
          public List<Department> call() throws Exception { 
            return departmentServiceImpl 
.readDepartments().get(500, TimeUnit.MILLISECONDS); 
          } 
      }; 
      return new WebAsyncTask<List<Department>>(500, callable); 
  } 
   
  @GetMapping(value="/deferSelectDept/{id}.json", 
produces ="application/json", 
headers = {"Accept=text/xml, application/json"}) 
  public DeferredResult<Department> deferredSelectDept( 
@PathVariable("id") Integer id) { 
      DeferredResult<Department> deferredResult = 
new DeferredResult<>(); 
      CompletableFuture.supplyAsync(()->{ 
          try { 
         return departmentServiceImpl 
.readDepartment(id).get(); 
           } catch (InterruptedException e) {   } 
             catch (ExecutionException e) {   } 
      return null; 
      }).thenAccept((msg)->{ 
        deferredResult.setResult(msg); 
      }); 
      return deferredResult; 
  } 
   
  @GetMapping(value="/callSelectDept/{id}.json", 
      produces ="application/json", 
      headers = {"Accept=text/xml, application/json"}) 
  public Callable<Department> jsonSoloEmployeeCall( 
            @PathVariable("id") Integer id){ 
    Callable<Department> task = new Callable<Department>() { 
            @Override 
            public Department call () throws Exception { 
                Department dept = departmentServiceImpl 
.readDepartment(id).get(); 
                return dept; 
            } 
        }; 
    return task; 
  } 
} 
  1. The last set of REST services are the reactive ones which can be implemented in two ways: using the typical @RestController and applying the functional and web framework APIs, namely the RouterFunction<T> and HandlerFunction<T> of Spring 5. The following are the reactive services for the department microservice implemented by @RestController:
@RestController 
public class DeptReactiveController { 
    
   @Autowired 
   private DepartmentService departmentServiceImpl; 
       
   @GetMapping("/selectReactDepts") 
   public Flux<Department> selectReactDepts() { 
      return Flux.fromIterable( 
departmentServiceImpl.findAllDepts()); 
   } 
    
   @GetMapping("/selectReactDept/{id}") 
   public Mono<Department> selectReactDept ( 
@PathVariable("id") Integer id) { 
      return Mono.justOrEmpty( 
departmentServiceImpl.findDeptByid(id)) 
          .defaultIfEmpty(new Department()); 
   } 
    
   @PostMapping("/saveReactDept") 
   public Mono<Void> saveReactDept( 
@RequestBody Department dept) { 
      return Mono.justOrEmpty(dept) 
.doOnNext(departmentServiceImpl::saveDeptRec).then(); 
   } 
}
  1. The following are the reactive REST services built by Spring 5's reactive functional web framework written inside @Configuration class:
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.RouterFunctions.route; 
@Configuration 
@EnableWebFlux 
public class DeptReactFuncControllers { 
    
   @Autowired 
   private DeptDataHandler dataHandler; 
    
   @Bean 
   public RouterFunction<ServerResponse>  
         departmentServiceBox(){ 
      return route(GET("/listFluxDepts"),  
         dataHandler::deptList) 
            .andRoute(GET("/selectDeptById/{id}"),  
                     dataHandler::chooseDeptById) 
            .andRoute(POST("/selectFluxDepts"),  
                     dataHandler::chooseFluxDepts) 
            .andRoute(POST("/saveFluxDept"),  
                     dataHandler::saveDepartmentMono) 
            .andRoute(GET("/countFluxDepts"),  
                     dataHandler::countDepts); 
   } 
} 
  1. Save all files. Build and deploy the reactive web project by running the Maven command clean spring-boot:run -U -e. Open a browser and execute all the GET services. If no errors and exceptions are found, name this application as the Department microservice.
  2. Copy the Login and Employee domains from the previous chapter and perform again all the processes in this recipe to build two more service boxes namely the Employee microserviceas ch10-empservice and Login microserviceas ch10-loginservice project.
..................Content has been hidden....................

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