├── 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 |
--------------------------------------------------------------------------------