3

In our Spring application we have the situation that we consumer several cloud-based REST endpoints to retrieve data that are combined into one single data object (let's call it Employee.java), which is then passed to the business logic.

I would like to encapsulate all these calls into one method of a service bean, something like this:

@Service
public class EmployeeService {

   public Employee retrieveEmployee(String id) {
      Employee e = new Employee();
      callRESTToFillWithBasicData(e);
      callRESTToFillWithExtendedData(e);
      callRESTToFillWithAdditionalData(e);
      return e;
   }
}

As the REST calls are rather time consuming, we want to make this method asynchronous, so the application can do other stuff in the meantime. For additional difficulty, the methods in the service need access to request-scoped Spring beans. The problem is that Spring doesn't by default pass the request context to threads spawned through async methods. Therefore we've written a custom implementation of ThreadPoolTaskExecutor, similar to what is suggested here. The asynchronous code looks like this:

@Service
public class EmployeeService {

   @Async("requestAwareTaskExecutor")
   public CompletableFuture<Employee> retrieveEmployee(String id) {
      Employee e = new Employee();
      callRESTToFillWithBasicData(e);
      callRESTToFillWithExtendedData(e);
      callRESTToFillWithAdditionalData(e);
      return CompletableFuture.completedFuture(e);
   }
}

Now, to speed the whole thing up some more, we also want to parallelize the 3 REST calls in the method. This leaves us with some problems to solve:

  • The problem is, we can't just put @Async on the callRESTToFill... methods, because method calls from one object to itself are not routed through the Spring proxy and thus not executed asynchronously.

  • We also can't just wrap the method calls in CompletableFuture.runAsync(...), because then the threads would, again, be unable to access request scoped beans.

  • The last resort would be to have the request-aware Executor bean @Autowired into the service bean and pass it to all the CompletableFuture calls.

But we don't feel that this is the Spring way to go. Directly using the Executor feels kind of superfluous, considering that we've already specified the executor bean in the @Async annotation.

What would be the best practice here?

ronin667
  • 323
  • 2
  • 15
  • I see 2 options - 1) Move the rest-call-methods to a different class (and therefore bean) and mark them as @Async 2) Use CGLIB as the proxying-mechanism. Once this is done, calls to methods within the same class are also intercepted – Ashutosh A Jun 06 '18 at 06:31
  • Actually, I see that CGLIB proxying may NOT intercept calls within the same object. This needs to be tested. Another option of-course is to use AspectJ mode for weaving the bytecode – Ashutosh A Jun 06 '18 at 08:05

0 Answers0