How to do it...

  1. Open the MessageController file and insert the following content:
package com.packtpub.microservices.ch03.message.controllers;

import com.packtpub.microservices.models.Message;
import com.packtpub.microservices.models.UserFriendships;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import java.net.URI;
import java.util.List;

@RestController
public class MessageController {

@RequestMapping(
path="/messages",
method=RequestMethod.POST,
produces="application/json")
public ResponseEntity<Message> create(@RequestBody Message message) {
List<String> friendships = getFriendsForUser(message.getFromUser(), message.getToUser());

if (friendships.isEmpty())
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();

URI location = ServletUriComponentsBuilder
.fromCurrentRequest().path("/{id}")
.buildAndExpand(message.getFromUser()).toUri();

return ResponseEntity.created(location).build();
}

private List<String> getFriendsForUser(String username, String filter) {
String url = "http://localhost:4567/friendships?username=" + username + "&filter=" + filter;
RestTemplate template = new RestTemplate();
UserFriendships friendships = template.getForObject(url, UserFriendships.class);
return friendships.getFriendships();
}
}
  1. Replace the getFriendsForUser method with a new method, called isFollowing. We give the new method an @Async annotation, which tells Spring Boot that this method will be run in a different thread:
import org.springframework.scheduling.annotation.Async;
import java.util.concurrent.CompletableFuture;

...

@Async
public CompletableFuture<Boolean> isFollowing(String fromUser, String toUser) {

String url = String.format(
"http://localhost:4567/followings?user=%s&filter=%s",
fromUser, toUser);

RestTemplate template = new RestTemplate();
UserFollowings followings = template.forObject(url, UserFollowings.class);

return CompletableFuture.completedFuture(
followings.getFollowings().isEmpty()
);
}
  1. Modify the create method to make the two service invocations. We'll need to wait until they are both done before deciding how to proceed, but the two service calls will be made concurrently:
@RequestMapping(
path="/messages",
method=RequestMethod.POST,
produces="application/json")
public ResponseEntity<Message> create(@RequestBody Message message) {

CompletableFuture<Boolean> result1 = isFollowing(message.getFromUser(), message.getToUser());
CompletableFuture<Boolean> result2 = isFollowing(message.getToUser(), message.getFromUser());

CompletableFuture.allOf(result1, result2).join();

// if both are not true, respond with a 403
if (!(result1.get() && result2.get()))
ResponseEntity.status(HttpStatus.FORBIDDEN).build();

... // proceed

}
  1. For the @Async annotation to schedule methods on separate threads, we need to configure an Executor. This is done in our Application class, as follows:
package com.packtpub.microservices;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@SpringBootApplication
@EnableAsync
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args).close();
}

@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(2);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("SocialServiceCall-");
executor.initialize();
return executor;
}


}

Our service now makes concurrent asynchronous calls to the social service in order to ensure that the sender and recipient of a message follow each other. We customize our Async scheduler with Executor defined as part of our application's configuration. We've configured our ThreadPoolTaskExecutor class to limit the number of threads to 2 and the queue size to 500. There are many factors to consider when configuring Executor, such as the amount of traffic you expect your service to receive and the average amount of time it takes for your service to serve a request. In this example, we'll leave it with these values.