How to do it...

Spring 5 offers asynchronous service layer that can be called by any asynchronous controllers. Let us build these service layer using the following steps:

  1. Before we start this recipe, the use of @Async requires a thorough and appropriate configuration of any TaskExecutor type in SpringAsynchConfig including some proxy-related configurations on @EnableAsync annotation.
  2. Create a package org.packt.web.reactor.service and add EmployeeService with some template methods:
public interface EmployeeService { 
   public CompletableFuture<List<Employee>>  
      readEmployees(); 
   public Callable<List<Employee>> readEmployeesCall(); 
   public Future<Employee>  readEmployee(Integer empId); 
   public void addEmployee(EmployeeForm emp); 
   public void updateEmployee(EmployeeForm emp, int id) ; 
public void delEmployee(Integer empId); 
}
  1. Any Spring 5 service can be converted to the asynchronous type just by having it return a Callable<T> task. Usually, it is mandatory for synchronous services to return Callable<T>, even when if it is created to be void by default. Create EmployeeServiceImpl by implementing readEmployeesCall() which retrieves a list of employees and wraps it with a Callable task:
@Service 
public class EmployeeServiceImpl implements EmployeeService { 
    
   @Autowired 
   private EmployeeDao employeeDaoImpl; 
 
@Override 
   public Callable<List<Employee>> readEmployeesCall() { 
      Callable< List<Employee> > task =  
new Callable< List<Employee> >() { 
            @Override 
            public  List<Employee>  call () throws  
               Exception { 
                                                         System.out.println("controller:readEmployeesCall  
task executor: " +  
   Thread.currentThread().getName()); 
             Thread.sleep(6000); 
             List<Employee> empList =  
                  employeeDaoImpl.getEmployees(); 
             return empList; 
            } 
        }; 
      return task; 
   } 
} 
  1. Another option for creating non-blocking services is to apply the @EnableAsync feature in the Spring platform. With this annotation, the @Async can now be attached to service methods with or without a return value, in order to run its transaction asynchronously:
@Async 
@Override 
public CompletableFuture<List<Employee>> readEmployees() { 
  Supplier<List<Employee>> supplyListEmp = ()->{ 
    System.out.println("service:readEmployees task  
    executor: " + Thread.currentThread().getName()); 
    System.out.println("processing for 5000 ms"); 
    try { 
      Thread.sleep(6000); 
    } catch (InterruptedException e) { 
      e.printStackTrace(); 
    }  
    return employeeDaoImpl.getEmployees(); 
  }; 
  return CompletableFuture.supplyAsync(supplyListEmp); 
} 
 
@Async 
@Override 
public void addEmployee(EmployeeForm empForm) { 
  
Employee emp = new Employee(); emp.setDeptId(empForm.getEmpId()); emp.setFirstName(empForm.getFirstName()); // refer to sources try { System.out.println("service:addEmployee task executor: " + Thread.currentThread().getName()); System.out.println("processing for 1000 ms"); Thread.sleep(1000); } catch (InterruptedException e) { } employeeDaoImpl.addEmployeeBySJI(emp); } @Async public Future<Employee> readEmployee(Integer empId) { try { System.out.println("service:readEmployee(empid) task executor: " + Thread.currentThread().getName()); System.out.println("processing for 2000 ms"); Thread.sleep(2000); } catch (InterruptedException e) { } return new AsyncResult<>(employeeDaoImpl.getEmployee(empId)); } @Async @Override public void updateEmployee(EmployeeForm empForm, int id) { Employee emp = new Employee(); emp.setDeptId(empForm.getEmpId()); emp.setFirstName(empForm.getFirstName()); // refer to sources try { System.out.println("service:updateEmployee task executor: " + thread.currentThread().getName()); System.out.println("processing for 1000 ms"); Thread.sleep(1000); } catch (InterruptedException e) { } employeeDaoImpl.updateEmployee(emp); } @Async @Override public void delEmployee(Integer empId) { try { System.out.println("service:delEmployee task executor: " + Thread.currentThread().getName()); System.out.println("processing for 1000 ms");
Thread.sleep(1000); } catch (InterruptedException e) { } employeeDaoImpl.delEmployee(empId); }
Combining @Async with Callable<T> will not work for Spring 5 service implementation due to some proxy-related issues.
  1. Now, create a full-blown form controller that will perform the adding of new employees and retrieving the list of employees from the data source using the recently implemented non-blocking methods:
@Controller 
@RequestMapping(value="/react/empform.html") 
public class EmployeeController { 
    
   @Autowired 
   private EmployeeService employeeServiceImpl; 
    
   @Autowired 
   private DepartmentService departmentServiceImpl; 
    
   @InitBinder("employeeForm") 
   public void initBinder(WebDataBinder binder){ 
      binder.registerCustomEditor(Integer.class, "age",  
new AgeEditor()); 
      binder.registerCustomEditor(Date.class,  
new DateEditor()); 
   } 
    
   @RequestMapping(method=RequestMethod.GET) 
   public String employeeForm(Model model){ 
      EmployeeForm employeeForm = new EmployeeForm(); 
      model.addAttribute("employeeForm", employeeForm); 
      references(model); 
      return "emp-form"; 
   } 
       
   @RequestMapping(method=RequestMethod.POST) 
   public String employeeList(Model model, @Validated  
      @ModelAttribute("employeeForm") EmployeeForm  
         employeeForm, BindingResult result){ 
      try { 
employeeServiceImpl.addEmployee(employeeForm); 
         List<Employee> empList = employeeServiceImpl 
.readEmployees().get(5000, TimeUnit.SECONDS); 
         model.addAttribute("empList", empList); 
      } catch (InterruptedException e) { }  
catch (ExecutionException e) { }  
catch (TimeoutException e) { } 
      return "emp-list"; 
   } 
    
   private void references(Model model){ 
      List<Integer> deptIds = new ArrayList<>(); 
      List<Department> depts =  
         departmentServiceImpl.readDepartments(); 
      Iterator<Department> iterate = depts.iterator(); 
      while(iterate.hasNext()){ 
         deptIds.add(iterate.next().getId()); 
      } 
      model.addAttribute("deptIds", deptIds); 
   } 
} 
  1. For creating reports and updating and deleting records of employees, we have this ReportController below that will showcase how to invoke @Async methods with Thread.sleep(n). The following request handler accesses the CompletableFuture<T> result from an asynchronous readDepartments() of DepartmentService through a risky get() method:
@Controller 
public class ReportController { 
    
   @Autowired 
   private DepartmentService departmentServiceImpl; 
    
   @Autowired 
   private EmployeeService employeeServiceImpl; 
    
   @RequestMapping(value="/react/viewdepts.html",  
      method=RequestMethod.GET) 
   public String viewDepts(Model model){ 
       
      try { 
         model.addAttribute("departments",  
            departmentServiceImpl 
.readDepartments().get(5000,  
      TimeUnit.MILLISECONDS)); 
      } catch (InterruptedException e) { }  
catch (ExecutionException e) { }  
catch (TimeoutException e) { } 
      return "dept-list"; 
   }
  1. Another way of retrieving result from CompletableFuture<T> task is through its non-risky join() method, which does not throw InterruptedException when something wrong happens during the asynchronous process:
@RequestMapping(value="/react/viewemps.html", method=RequestMethod.GET) 
public String viewEmps(Model model){ 
  List<Employee> empList = employeeServiceImpl.readEmployees().join(); 
  model.addAttribute("empList", empList); 
  return "emp-list"; 
} 
  1. There are @Async methods that perform asynchronous with the help of Thread.sleep() just to delay the process and to avoid thread-related Exception like the following delEmployee() of EmployeeService:
@RequestMapping(value={"/react/delemp.html/{empId}"}) 
public String deleteRecord(Model model, @PathVariable("empId") Integer empId){ 
       
  try { 
    employeeServiceImpl.delEmployee(empId); 
    Thread.sleep(1000); 
    List<Employee> empList = employeeServiceImpl
.readEmployees().get(5000, TimeUnit.SECONDS); model.addAttribute("empList", empList); } catch (InterruptedException e) { } catch (ExecutionException e) { } catch (TimeoutException e) { } return "emp-list"; }
  1. One way of executing asynchronous services is to use the supplyAsync() static method of CompletableFuture<T> that requires a Supplier<T> functional interface, as shown by the following request handler:
@RequestMapping(value={"/react/updateemp.html/{id}"},
method=RequestMethod.POST) public String updateRecordSubmit(Model model,@PathVariable("id") Integer id, @Validated @ModelAttribute("employeeForm") EmployeeForm employeeForm, BindingResult result){ Consumer<List<Employee>> processResult = (empList) ->{ model.addAttribute("empList", empList); }; Supplier<List<Employee>> asyncSupplier = () ->{ try { employeeServiceImpl.updateEmployee(employeeForm, id); Thread.sleep(1000); return employeeServiceImpl.readEmployees().get(5000, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } return null; }; CompletableFuture.supplyAsync(asyncSupplier)
.thenAccept(processResult); return "emp-list"; } private void references(Model model){ List<Integer> deptIds = new ArrayList<>(); List<Department> depts = departmentServiceImpl .readDepartments().getNow(new ArrayList<>()); // refer to sources model.addAttribute("deptIds", deptIds); }
  1. Use the EmployeeDao implementation from Chapter 3, Implementing MVC Design Pattern, on JDBC concepts.
  2. Import the AgeEditor and DateEditor of the previous chapters.
  3. Utilize the message bundles, view mappings and view pages from Chapter 3, Implementing MVC Design Pattern.
  4. Save all files. Then clean, build, and run transactions. Run each request handler several times and observe /logs/tomcat9-stdout.xxxx-xx-xx.log:
service:readEmployees task executor: pool-3-thread-4
processing for 5000 ms
service:addEmployee task executor: pool-3-thread-3
processing for 1000 ms
Thread pool-3-thread-3 has a processing time:  1012
Thread pool-3-thread-4 has a processing time:  5003
service:delEmployee task executor: pool-3-thread-5
processing for 1000 ms
service:readEmployees task executor: pool-3-thread-6
processing for 5000 ms
Thread pool-3-thread-5 has a processing time:  1072
Thread pool-3-thread-6 has a processing time:  5002  
..................Content has been hidden....................

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