├── Cars.csv ├── LICENSE ├── README.md ├── pom.xml └── src └── main └── java └── com └── techshard └── future ├── Application.java ├── AsyncConfiguration.java ├── controller └── CarController.java ├── dao ├── entity │ └── Car.java └── repository │ └── CarRepository.java └── service └── CarService.java /Cars.csv: -------------------------------------------------------------------------------- 1 | Acura;Integra;Small 2 | Acura;Legend;Midsize 3 | Audi;90;Compact 4 | Audi;100;Midsize 5 | BMW;535i;Midsize 6 | Buick;Century;Midsize 7 | Buick;LeSabre;Large 8 | Buick;Roadmaster;Large 9 | Buick;Riviera;Midsize 10 | Cadillac;DeVille;Large 11 | Cadillac;Seville;Midsize 12 | Chevrolet;Cavalier;Compact 13 | Chevrolet;Corsica;Compact 14 | Chevrolet;Camaro;Sporty 15 | Chevrolet;Lumina;Midsize 16 | Chevrolet;Lumina_APV;Van 17 | Chevrolet;Astro;Van 18 | Chevrolet;Caprice;Large 19 | Chevrolet;Corvette;Sporty 20 | Chrylser;Concorde;Large 21 | Chrysler;LeBaron;Compact 22 | Chrysler;Imperial;Large 23 | Dodge;Colt;Small 24 | Dodge;Shadow;Small 25 | Dodge;Spirit;Compact 26 | Dodge;Caravan;Van 27 | Dodge;Dynasty;Midsize 28 | Dodge;Stealth;Sporty 29 | Eagle;Summit;Small 30 | Eagle;Vision;Large 31 | Ford;Festiva;Small 32 | Ford;Escort;Small 33 | Ford;Tempo;Compact 34 | Ford;Mustang;Sporty 35 | Ford;Probe;Sporty 36 | Ford;Aerostar;Van 37 | Ford;Taurus;Midsize 38 | Ford;Crown_Victoria;Large 39 | Geo;Metro;Small 40 | Geo;Storm;Sporty 41 | Honda;Prelude;Sporty 42 | Honda;Civic;Small 43 | Honda;Accord;Compact 44 | Hyundai;Excel;Small 45 | Hyundai;Elantra;Small 46 | Hyundai;Scoupe;Sporty 47 | Hyundai;Sonata;Midsize 48 | Infiniti;Q45;Midsize 49 | Lexus;ES300;Midsize 50 | Lexus;SC300;Midsize 51 | Lincoln;Continental;Midsize 52 | Lincoln;Town_Car;Large 53 | Mazda;323;Small 54 | Mazda;Protege;Small 55 | Mazda;626;Compact 56 | Mazda;MPV;Van 57 | Mazda;RX-7;Sporty 58 | Mercedes-Benz;190E;Compact 59 | Mercedes-Benz;300E;Midsize 60 | Mercury;Capri;Sporty 61 | Mercury;Cougar;Midsize 62 | Mitsubishi;Mirage;Small 63 | Mitsubishi;Diamante;Midsize 64 | Nissan;Sentra;Small 65 | Nissan;Altima;Compact 66 | Nissan;Quest;Van 67 | Nissan;Maxima;Midsize 68 | Oldsmobile;Achieva;Compact 69 | Oldsmobile;Cutlass_Ciera;Midsize 70 | Oldsmobile;Silhouette;Van 71 | Oldsmobile;Eighty-Eight;Large 72 | Plymouth;Laser;Sporty 73 | Pontiac;LeMans;Small 74 | Pontiac;Sunbird;Compact 75 | Pontiac;Firebird;Sporty 76 | Pontiac;Grand_Prix;Midsize 77 | Pontiac;Bonneville;Large 78 | Saab;900;Compact 79 | Saturn;SL;Small 80 | Subaru;Justy;Small 81 | Subaru;Loyale;Small 82 | Subaru;Legacy;Compact 83 | Suzuki;Swift;Small 84 | Toyota;Tercel;Small 85 | Toyota;Celica;Sporty 86 | Toyota;Camry;Midsize 87 | Toyota;Previa;Van 88 | Volkswagen;Fox;Small 89 | Volkswagen;Eurovan;Van 90 | Volkswagen;Passat;Compact 91 | Volkswagen;Corrado;Sporty 92 | Volvo;240;Compact 93 | Volvo;850;Midsize -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Swathi Prasad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-boot-completable-future 2 | Creating Asynchronous Methods using Completable Future in Spring Boot 3 | 4 | https://techshard.com/2019/09/15/multi-threading-in-spring-boot-using-completablefuture/ 5 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.techshard.future 8 | springboot-future 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.1.8.RELEASE 15 | 16 | 17 | 18 | 19 | UTF-8 20 | UTF-8 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-jpa 31 | 32 | 33 | com.h2database 34 | h2 35 | runtime 36 | 37 | 38 | org.slf4j 39 | slf4j-api 40 | 41 | 42 | org.projectlombok 43 | lombok 44 | 1.18.10 45 | true 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/com/techshard/future/Application.java: -------------------------------------------------------------------------------- 1 | package com.techshard.future; 2 | 3 | 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 7 | 8 | @SpringBootApplication 9 | public class Application extends SpringBootServletInitializer{ 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(Application.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/techshard/future/AsyncConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.techshard.future; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.scheduling.annotation.EnableAsync; 8 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 9 | 10 | import java.util.concurrent.Executor; 11 | 12 | @Configuration 13 | @EnableAsync 14 | public class AsyncConfiguration { 15 | 16 | private static final Logger LOGGER = LoggerFactory.getLogger(AsyncConfiguration.class); 17 | 18 | @Bean (name = "taskExecutor") 19 | public Executor taskExecutor() { 20 | LOGGER.debug("Creating Async Task Executor"); 21 | final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 22 | executor.setCorePoolSize(2); 23 | executor.setMaxPoolSize(2); 24 | executor.setQueueCapacity(25); 25 | executor.setThreadNamePrefix("CarThread-"); 26 | executor.initialize(); 27 | return executor; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/techshard/future/controller/CarController.java: -------------------------------------------------------------------------------- 1 | package com.techshard.future.controller; 2 | 3 | import com.techshard.future.dao.entity.Car; 4 | import com.techshard.future.service.CarService; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.*; 12 | import org.springframework.web.multipart.MultipartFile; 13 | 14 | import java.io.File; 15 | import java.util.List; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.function.Function; 18 | 19 | @RestController 20 | @RequestMapping("/api/car") 21 | public class CarController { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(CarController.class); 24 | 25 | @Autowired 26 | private CarService carService; 27 | 28 | @RequestMapping (method = RequestMethod.POST, consumes={MediaType.MULTIPART_FORM_DATA_VALUE}, 29 | produces={MediaType.APPLICATION_JSON_VALUE}) 30 | public @ResponseBody ResponseEntity uploadFile( 31 | @RequestParam (value = "files") MultipartFile[] files) { 32 | try { 33 | for(final MultipartFile file: files) { 34 | carService.saveCars(file.getInputStream()); 35 | } 36 | return ResponseEntity.status(HttpStatus.CREATED).build(); 37 | } catch(final Exception e) { 38 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); 39 | } 40 | 41 | } 42 | 43 | @RequestMapping (method = RequestMethod.GET, consumes={MediaType.APPLICATION_JSON_VALUE}, 44 | produces={MediaType.APPLICATION_JSON_VALUE}) 45 | public @ResponseBody CompletableFuture getAllCars() { 46 | return carService.getAllCars().thenApply(ResponseEntity::ok) 47 | .exceptionally(handleGetCarFailure); 48 | } 49 | 50 | private static Function>> handleGetCarFailure = throwable -> { 51 | LOGGER.error("Failed to read records: {}", throwable); 52 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/techshard/future/dao/entity/Car.java: -------------------------------------------------------------------------------- 1 | package com.techshard.future.dao.entity; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | import javax.persistence.*; 7 | import javax.validation.constraints.NotNull; 8 | import java.io.Serializable; 9 | 10 | @Data 11 | @EqualsAndHashCode 12 | @Entity 13 | public class Car implements Serializable { 14 | 15 | private static final long serialVersionUID = 1L; 16 | 17 | @Id 18 | @Column (name = "ID", nullable = false) 19 | @GeneratedValue (strategy = GenerationType.IDENTITY) 20 | private long id; 21 | 22 | @NotNull 23 | @Column(nullable=false) 24 | private String manufacturer; 25 | 26 | @NotNull 27 | @Column(nullable=false) 28 | private String model; 29 | 30 | @NotNull 31 | @Column(nullable=false) 32 | private String type; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/techshard/future/dao/repository/CarRepository.java: -------------------------------------------------------------------------------- 1 | package com.techshard.future.dao.repository; 2 | 3 | import com.techshard.future.dao.entity.Car; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface CarRepository extends JpaRepository { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/techshard/future/service/CarService.java: -------------------------------------------------------------------------------- 1 | package com.techshard.future.service; 2 | 3 | import com.techshard.future.dao.entity.Car; 4 | import com.techshard.future.dao.repository.CarRepository; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.scheduling.annotation.Async; 9 | import org.springframework.stereotype.Service; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | import java.io.*; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.concurrent.CompletableFuture; 16 | 17 | @Service 18 | public class CarService { 19 | 20 | private static final Logger LOGGER = LoggerFactory.getLogger(CarService.class); 21 | 22 | @Autowired 23 | private CarRepository carRepository; 24 | 25 | @Async 26 | public CompletableFuture> saveCars(final InputStream inputStream) throws Exception { 27 | final long start = System.currentTimeMillis(); 28 | 29 | List cars = parseCSVFile(inputStream); 30 | 31 | LOGGER.info("Saving a list of cars of size {} records", cars.size()); 32 | 33 | cars = carRepository.saveAll(cars); 34 | 35 | LOGGER.info("Elapsed time: {}", (System.currentTimeMillis() - start)); 36 | return CompletableFuture.completedFuture(cars); 37 | } 38 | 39 | private List parseCSVFile(final InputStream inputStream) throws Exception { 40 | final List cars=new ArrayList<>(); 41 | try { 42 | try (final BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { 43 | String line; 44 | while ((line=br.readLine()) != null) { 45 | final String[] data=line.split(";"); 46 | final Car car=new Car(); 47 | car.setManufacturer(data[0]); 48 | car.setModel(data[1]); 49 | car.setType(data[2]); 50 | cars.add(car); 51 | } 52 | return cars; 53 | } 54 | } catch(final IOException e) { 55 | LOGGER.error("Failed to parse CSV file {}", e); 56 | throw new Exception("Failed to parse CSV file {}", e); 57 | } 58 | } 59 | 60 | @Async 61 | public CompletableFuture> getAllCars() { 62 | 63 | LOGGER.info("Request to get a list of cars"); 64 | 65 | final List cars = carRepository.findAll(); 66 | return CompletableFuture.completedFuture(cars); 67 | } 68 | } 69 | --------------------------------------------------------------------------------