From ac30817a2365e294a2d9fb2a453bd22182f73c45 Mon Sep 17 00:00:00 2001 From: Swapan Pramanick Date: Fri, 2 Apr 2021 12:51:43 +0200 Subject: [PATCH 1/7] Evaluation Article: A quick and practical example of Hexagonal Architecture in Java --- .../hexagonal/HexagonalSpringApplication.java | 20 +++ .../adapter/BookingPersistenceAdapter.java | 18 +++ .../adapter/RestAPIEndpointAdapter.java | 38 ++++++ .../adapter/TheatreServiceAdapter.java | 31 +++++ .../adapter/WalletServiceAdapter.java | 23 ++++ .../baeldung/hexagonal/domain/Booking.java | 84 +++++++++++++ .../service/CustomerWalletService.java | 8 ++ .../service/MockCustomerWalletService.java | 11 ++ .../external/service/MockTheatreService.java | 21 ++++ .../external/service/TheatreService.java | 24 ++++ .../port/BookingPersistencePort.java | 7 ++ .../hexagonal/port/BookingServicePort.java | 73 +++++++++++ .../hexagonal/port/TheatreServicePort.java | 9 ++ .../hexagonal/port/WalletServicePort.java | 5 + .../repository/BookingRepository.java | 8 ++ .../repository/MockBookingRepository.java | 11 ++ .../hexagonal/usecase/BookTicketUseCase.java | 67 ++++++++++ .../RestAPIEndpointAdapterUnitTest.java | 88 +++++++++++++ .../usecase/BookTicketUseCaseUnitTest.java | 119 ++++++++++++++++++ 19 files changed, 665 insertions(+) create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java create mode 100644 ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java create mode 100644 ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java create mode 100644 ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java diff --git a/ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java b/ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java new file mode 100644 index 0000000000..c679d459f0 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java @@ -0,0 +1,20 @@ +package com.baeldung.hexagonal; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; + + +@SpringBootApplication(exclude={ + CassandraAutoConfiguration.class, + MongoDataAutoConfiguration.class, + MongoAutoConfiguration.class +}) +public class HexagonalSpringApplication { + + public static void main(final String[] args) { + SpringApplication.run(HexagonalSpringApplication.class, args); + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java new file mode 100644 index 0000000000..e6d4dbbe36 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java @@ -0,0 +1,18 @@ +package com.baeldung.hexagonal.adapter; + +import com.baeldung.hexagonal.domain.Booking; +import com.baeldung.hexagonal.port.BookingPersistencePort; +import com.baeldung.hexagonal.repository.BookingRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class BookingPersistenceAdapter implements BookingPersistencePort { + + @Autowired + private BookingRepository bookingRepository; + + public boolean persist(Booking booking) { + return bookingRepository.save(booking); + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java new file mode 100644 index 0000000000..7c35f302c0 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java @@ -0,0 +1,38 @@ +package com.baeldung.hexagonal.adapter; + +import com.baeldung.hexagonal.port.BookingServicePort; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import static com.baeldung.hexagonal.port.BookingServicePort.BookingRequest; +import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse; +import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*; + +@RestController +public class RestAPIEndpointAdapter { + + private BookingServicePort bookingServicePort; + + @Autowired + public RestAPIEndpointAdapter(BookingServicePort bookingServicePort) { + this.bookingServicePort = bookingServicePort; + } + + @PostMapping(path = "/booking") + public ResponseEntity createBooking(@RequestBody BookingRequest request) { + BookingResponse response = bookingServicePort.book(request); + + if (response.getStatusCode() == SEAT_NOT_AVAILABLE + || response.getStatusCode() == PAYMENT_FAILED){ + return new ResponseEntity(response, HttpStatus.PRECONDITION_FAILED); + } else if (response.getStatusCode() == UNKNOWN_ERROR) { + return new ResponseEntity(response, HttpStatus.INTERNAL_SERVER_ERROR); + } + + return new ResponseEntity(response, HttpStatus.CREATED); + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java new file mode 100644 index 0000000000..d644f5eee0 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java @@ -0,0 +1,31 @@ +package com.baeldung.hexagonal.adapter; + +import com.baeldung.hexagonal.external.service.TheatreService; +import com.baeldung.hexagonal.port.TheatreServicePort; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.Set; + +@Component +public class TheatreServiceAdapter implements TheatreServicePort { + + @Autowired + private TheatreService theatreService; + + public Optional reserveSeats(String theatreId, String movieShowId, Set seats) { + ResponseEntity response = theatreService.postReservation(theatreId, movieShowId, seats); + if (response.getStatusCode() == HttpStatus.CREATED) { + return Optional.of(response.getBody().getId()); + } + return Optional.empty(); + } + + public boolean releaseSeats(String resrevationId) { + ResponseEntity response = theatreService.deleteReservation(resrevationId); + return response.getStatusCode() == HttpStatus.NO_CONTENT; + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java new file mode 100644 index 0000000000..930b9c1cf6 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java @@ -0,0 +1,23 @@ +package com.baeldung.hexagonal.adapter; + +import com.baeldung.hexagonal.external.service.CustomerWalletService; +import com.baeldung.hexagonal.port.WalletServicePort; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +@Component +public class WalletServiceAdapter implements WalletServicePort { + + private final CustomerWalletService customerWalletService; + + @Autowired + public WalletServiceAdapter(CustomerWalletService customerWalletService) { + this.customerWalletService = customerWalletService; + } + + public boolean debit(String customerId, Double amount) { + HttpStatus response = customerWalletService.postDebit(customerId, amount); + return response == HttpStatus.CREATED; + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java b/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java new file mode 100644 index 0000000000..34e3f3b81b --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java @@ -0,0 +1,84 @@ +package com.baeldung.hexagonal.domain; + +import java.util.Set; + +public class Booking { + private String bookingId; + private String movieShowId; + private String theatreId; + private String customerId; + private Set seats; + private Double amount; + private Status status; + + public enum Status { + INITIAL, SUCCESS, FAILURE + } + + public Booking( + String bookingId, String movieShowId, String theatreId, String customerId, Set seats, Double amount, Status status) { + this.bookingId = bookingId; + this.movieShowId = movieShowId; + this.theatreId = theatreId; + this.customerId = customerId; + this.seats = seats; + this.amount = amount; + this.status = Status.INITIAL; + } + + public String getMovieShowId() { + return movieShowId; + } + + public String getTheatreId() { + return theatreId; + } + + public Set getSeats() { + return seats; + } + + public String getCustomerId() { + return customerId; + } + + public String getBookingId() { + return bookingId; + } + + public Double getAmount() { + return amount; + } + + public Status getStatus() { + return status; + } + + public void setBookingId(String bookingId) { + this.bookingId = bookingId; + } + + public void setMovieShowId(String movieShowId) { + this.movieShowId = movieShowId; + } + + public void setTheatreId(String theatreId) { + this.theatreId = theatreId; + } + + public void setCustomerId(String customerId) { + this.customerId = customerId; + } + + public void setSeats(Set seats) { + this.seats = seats; + } + + public void setAmount(Double amount) { + this.amount = amount; + } + + public void setStatus(Status status) { + this.status = status; + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java new file mode 100644 index 0000000000..102cef788e --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java @@ -0,0 +1,8 @@ +package com.baeldung.hexagonal.external.service; + +import org.springframework.http.HttpStatus; + +public interface CustomerWalletService { + + HttpStatus postDebit(String customerId, Double amount); +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java new file mode 100644 index 0000000000..4a76368b19 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java @@ -0,0 +1,11 @@ +package com.baeldung.hexagonal.external.service; + +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +@Service +public class MockCustomerWalletService implements CustomerWalletService { + public HttpStatus postDebit(String customerId, Double amount) { + return HttpStatus.CREATED; + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java new file mode 100644 index 0000000000..333cbee889 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java @@ -0,0 +1,21 @@ +package com.baeldung.hexagonal.external.service; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.util.Set; +import java.util.UUID; + +@Service +public class MockTheatreService implements TheatreService { + + public ResponseEntity postReservation(String theatreId, String movieShowId, Set seats) { + return new ResponseEntity( + new Reservation(UUID.randomUUID().toString()), HttpStatus.CREATED); + } + + public ResponseEntity deleteReservation(String reservationId) { + return new ResponseEntity(HttpStatus.NO_CONTENT); + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java new file mode 100644 index 0000000000..48f0c5e15d --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java @@ -0,0 +1,24 @@ +package com.baeldung.hexagonal.external.service; + +import org.springframework.http.ResponseEntity; + +import java.util.Set; + +public interface TheatreService { + + ResponseEntity postReservation(String theatreId, String movieShowId, Set seats); + + ResponseEntity deleteReservation(String reservationId); + + class Reservation { + private final String id; + + public Reservation(String id) { + this.id = id; + } + + public String getId() { + return id; + } + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java new file mode 100644 index 0000000000..0deffb42e0 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java @@ -0,0 +1,7 @@ +package com.baeldung.hexagonal.port; + +import com.baeldung.hexagonal.domain.Booking; + +public interface BookingPersistencePort { + boolean persist(Booking booking); +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java new file mode 100644 index 0000000000..3b444df4f6 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java @@ -0,0 +1,73 @@ +package com.baeldung.hexagonal.port; + +import java.util.Set; + +public interface BookingServicePort { + + BookingResponse book(BookingRequest request); + + class BookingRequest { + private String movieShowId; + private String customerId; + private String theatreId; + private Set seats; + private Double amount; + + public String getMovieShowId() { + return movieShowId; + } + + public void setMovieShowId(String movieShowId) { + this.movieShowId = movieShowId; + } + + public String getCustomerId() { + return customerId; + } + + public void setCustomerId(String customerId) { + this.customerId = customerId; + } + + public String getTheatreId() { + return theatreId; + } + + public void setTheatreId(String theatreId) { + this.theatreId = theatreId; + } + + public Set getSeats() { + return seats; + } + + public void setSeats(Set seats) { + this.seats = seats; + } + + public Double getAmount() { + return amount; + } + + public void setAmount(Double amount) { + this.amount = amount; + } + } + + class BookingResponse { + public static final int SUCCESS = 0; + public static final int SEAT_NOT_AVAILABLE = 1; + public static final int PAYMENT_FAILED = 2; + public static final int UNKNOWN_ERROR = 3; + + private final int statusCode; + + public BookingResponse(int statusCode) { + this.statusCode = statusCode; + } + + public int getStatusCode() { + return statusCode; + } + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java new file mode 100644 index 0000000000..dac517c724 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java @@ -0,0 +1,9 @@ +package com.baeldung.hexagonal.port; + +import java.util.Optional; +import java.util.Set; + +public interface TheatreServicePort { + Optional reserveSeats(String theatreId, String movieShowId, Set seats); + boolean releaseSeats(String resrevationId); +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java new file mode 100644 index 0000000000..102bb619e8 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java @@ -0,0 +1,5 @@ +package com.baeldung.hexagonal.port; + +public interface WalletServicePort { + boolean debit(String customerId, Double amount); +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java b/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java new file mode 100644 index 0000000000..c56ffe1308 --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java @@ -0,0 +1,8 @@ +package com.baeldung.hexagonal.repository; + +import com.baeldung.hexagonal.domain.Booking; + +public interface BookingRepository { + + boolean save(Booking booking); +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java b/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java new file mode 100644 index 0000000000..d378cff0af --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java @@ -0,0 +1,11 @@ +package com.baeldung.hexagonal.repository; + +import com.baeldung.hexagonal.domain.Booking; +import org.springframework.stereotype.Repository; + +@Repository +public class MockBookingRepository implements BookingRepository { + public boolean save(Booking booking) { + return true; + } +} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java b/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java new file mode 100644 index 0000000000..7d9db9724e --- /dev/null +++ b/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java @@ -0,0 +1,67 @@ +package com.baeldung.hexagonal.usecase; + +import com.baeldung.hexagonal.domain.Booking; +import com.baeldung.hexagonal.port.BookingPersistencePort; +import com.baeldung.hexagonal.port.BookingServicePort; +import com.baeldung.hexagonal.port.TheatreServicePort; +import com.baeldung.hexagonal.port.WalletServicePort; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Optional; +import java.util.UUID; + +import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*; + +@Component +public class BookTicketUseCase implements BookingServicePort { + + private BookingPersistencePort bookingPersistencePort; + private TheatreServicePort theatreServicePort; + private WalletServicePort walletServicePort; + + @Autowired + public BookTicketUseCase(BookingPersistencePort bookingPersistencePort, TheatreServicePort theatreServicePort, WalletServicePort walletServicePort) { + this.bookingPersistencePort = bookingPersistencePort; + this.theatreServicePort = theatreServicePort; + this.walletServicePort = walletServicePort; + } + + public BookingResponse book(BookingRequest request) { + + Booking booking = new Booking( + UUID.randomUUID().toString(), + request.getMovieShowId(), + request.getTheatreId(), + request.getCustomerId(), + request.getSeats(), + request.getAmount(), + Booking.Status.INITIAL); + + if (!bookingPersistencePort.persist(booking)) { + return new BookingResponse(UNKNOWN_ERROR); + } + + Optional reservationIdOptional = theatreServicePort.reserveSeats( + booking.getTheatreId(), + booking.getMovieShowId(), + booking.getSeats()); + if (!reservationIdOptional.isPresent()) { + booking.setStatus(Booking.Status.FAILURE); + bookingPersistencePort.persist(booking); + return new BookingResponse(SEAT_NOT_AVAILABLE); + } + + if (!walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) { + reservationIdOptional.ifPresent(reservationId -> theatreServicePort.releaseSeats(reservationId)); + booking.setStatus(Booking.Status.FAILURE); + bookingPersistencePort.persist(booking); + return new BookingResponse(PAYMENT_FAILED); + } + + booking.setStatus(Booking.Status.SUCCESS); + bookingPersistencePort.persist(booking); + + return new BookingResponse(SUCCESS); + } +} diff --git a/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java b/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java new file mode 100644 index 0000000000..76d853fdda --- /dev/null +++ b/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java @@ -0,0 +1,88 @@ +package com.baeldung.hexagonal.adapter; + +import com.baeldung.hexagonal.port.BookingServicePort; +import com.baeldung.hexagonal.port.BookingServicePort.BookingRequest; +import com.baeldung.hexagonal.port.BookingServicePort.BookingResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.util.Arrays; +import java.util.HashSet; + +import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class RestAPIEndpointAdapterUnitTest { + + private BookingServicePort bookingServicePort; + private RestAPIEndpointAdapter restAPIEndpointAdapter; + + @BeforeEach + void setUp() { + bookingServicePort = mock(BookingServicePort.class); + restAPIEndpointAdapter = new RestAPIEndpointAdapter(bookingServicePort); + } + + private BookingServicePort.BookingRequest getBookingRequest() { + BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest(); + request.setTheatreId("theatre-id"); + request.setMovieShowId("movie-show-id"); + request.setCustomerId("customer-id"); + request.setSeats(new HashSet<>(Arrays.asList("A1", "A2"))); + request.setAmount(100.00); + return request; + } + + @Test + void whenBookingServicePortReturnsUnknownError_thenReturnInternalServerError() { + BookingServicePort.BookingRequest request = getBookingRequest(); + when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(UNKNOWN_ERROR)); + + ResponseEntity response = restAPIEndpointAdapter.createBooking(request); + + verify(bookingServicePort).book(any(BookingRequest.class)); + assertNotNull(response); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + } + + @Test + void whenBookingServicePortReturnsSeatUnavailable_thenReturnPreconditionFailed() { + BookingServicePort.BookingRequest request = getBookingRequest(); + when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(SEAT_NOT_AVAILABLE)); + + ResponseEntity response = restAPIEndpointAdapter.createBooking(request); + + verify(bookingServicePort).book(any(BookingRequest.class)); + assertNotNull(response); + assertEquals(HttpStatus.PRECONDITION_FAILED, response.getStatusCode()); + } + + @Test + void whenBookingServicePortReturnsPaymentFailed_thenReturnPreconditionFailed() { + BookingServicePort.BookingRequest request = getBookingRequest(); + when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(PAYMENT_FAILED)); + + ResponseEntity response = restAPIEndpointAdapter.createBooking(request); + + verify(bookingServicePort).book(any(BookingRequest.class)); + assertNotNull(response); + assertEquals(HttpStatus.PRECONDITION_FAILED, response.getStatusCode()); + } + + @Test + void whenBookingServicePortReturnsSuccess_thenReturnCreated() { + BookingServicePort.BookingRequest request = getBookingRequest(); + when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(SUCCESS)); + + ResponseEntity response = restAPIEndpointAdapter.createBooking(request); + + verify(bookingServicePort).book(any(BookingRequest.class)); + assertNotNull(response); + assertEquals(HttpStatus.CREATED, response.getStatusCode()); + } +} \ No newline at end of file diff --git a/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java b/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java new file mode 100644 index 0000000000..eec2b65609 --- /dev/null +++ b/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java @@ -0,0 +1,119 @@ +package com.baeldung.hexagonal.usecase; + +import com.baeldung.hexagonal.domain.Booking; +import com.baeldung.hexagonal.port.BookingPersistencePort; +import com.baeldung.hexagonal.port.BookingServicePort; +import com.baeldung.hexagonal.port.TheatreServicePort; +import com.baeldung.hexagonal.port.WalletServicePort; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class BookTicketUseCaseUnitTest { + + private BookingPersistencePort bookingPersistencePort; + private TheatreServicePort theatreServicePort; + private WalletServicePort walletServicePort; + private BookTicketUseCase bookTicketUseCase; + + @BeforeEach + void setUp() { + bookingPersistencePort = mock(BookingPersistencePort.class); + theatreServicePort = mock(TheatreServicePort.class); + walletServicePort = mock(WalletServicePort.class); + bookTicketUseCase = new BookTicketUseCase(bookingPersistencePort, theatreServicePort, walletServicePort); + } + + private BookingServicePort.BookingRequest getBookingRequest() { + BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest(); + request.setTheatreId("theatre-id"); + request.setMovieShowId("movie-show-id"); + request.setCustomerId("customer-id"); + request.setSeats(new HashSet<>(Arrays.asList("A1", "A2"))); + request.setAmount(100.00); + return request; + } + + private Booking getBooking(BookingServicePort.BookingRequest request) { + return new Booking( + "booking-id", + request.getMovieShowId(), + request.getTheatreId(), + request.getCustomerId(), + request.getSeats(), + request.getAmount(), + Booking.Status.INITIAL); + } + + @Test + void whenErrorInInitialPersistence_thenReturnUnknownError() { + BookingServicePort.BookingRequest request = getBookingRequest(); + Booking booking = getBooking(request); + when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(false); + BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); + + verify(bookingPersistencePort, times(1)).persist(any(Booking.class)); + assertNotNull(response); + assertEquals(BookingServicePort.BookingResponse.UNKNOWN_ERROR, response.getStatusCode()); + } + + @Test + void whenErrorInReserveSeats_thenReturnSeatNotAvailable() { + BookingServicePort.BookingRequest request = getBookingRequest(); + Booking booking = getBooking(request); + when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); + when(theatreServicePort.reserveSeats(booking.getTheatreId(), booking.getMovieShowId(), booking.getSeats())) + .thenReturn(Optional.empty()); + BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); + + verify(bookingPersistencePort, times(2)).persist(any(Booking.class)); + verify(theatreServicePort).reserveSeats(any(String.class), any(String.class), any(HashSet.class)); + assertNotNull(response); + assertEquals(BookingServicePort.BookingResponse.SEAT_NOT_AVAILABLE, response.getStatusCode()); + } + + @Test + void whenErrorInWalletDebit_thenReturnPaymentFailed() { + BookingServicePort.BookingRequest request = getBookingRequest(); + Booking booking = getBooking(request); + when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); + when(theatreServicePort.reserveSeats(booking.getTheatreId(), booking.getMovieShowId(), booking.getSeats())) + .thenReturn(Optional.of("reservation-id")); + when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) + .thenReturn(false); + BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); + + verify(bookingPersistencePort, times(2)).persist(any(Booking.class)); + verify(theatreServicePort).reserveSeats(any(String.class), any(String.class), any(HashSet.class)); + verify(walletServicePort).debit(any(String.class), any(Double.class)); + verify(theatreServicePort).releaseSeats(any(String.class)); + assertNotNull(response); + assertEquals(BookingServicePort.BookingResponse.PAYMENT_FAILED, response.getStatusCode()); + } + + @Test + void whenNoErrorInAnyPorts_thenReturnSuccess() { + BookingServicePort.BookingRequest request = getBookingRequest(); + Booking booking = getBooking(request); + when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); + when(theatreServicePort.reserveSeats(booking.getTheatreId(), booking.getMovieShowId(), booking.getSeats())) + .thenReturn(Optional.of("reservation-id")); + when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) + .thenReturn(true); + BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); + + verify(bookingPersistencePort, times(2)).persist(any(Booking.class)); + verify(theatreServicePort).reserveSeats(any(String.class), any(String.class), any(HashSet.class)); + verify(walletServicePort).debit(any(String.class), any(Double.class)); + assertNotNull(response); + assertEquals(BookingServicePort.BookingResponse.SUCCESS, response.getStatusCode()); + } +} \ No newline at end of file From c5b651fca0334a707e3cd8cd443b52ec35b0011e Mon Sep 17 00:00:00 2001 From: Swapan Pramanick Date: Fri, 9 Apr 2021 20:31:53 +0200 Subject: [PATCH 2/7] Update code to be more compact --- .../adapter/BookingPersistenceAdapter.java | 4 ++ .../adapter/RestAPIEndpointAdapter.java | 16 +++----- .../adapter/TheatreServiceAdapter.java | 17 ++++---- .../adapter/WalletServiceAdapter.java | 12 ++---- .../baeldung/hexagonal/domain/Booking.java | 41 ++++++++++--------- .../external/service/MockTheatreService.java | 8 ++-- .../external/service/TheatreService.java | 4 +- .../port/BookingPersistencePort.java | 1 + .../hexagonal/port/BookingServicePort.java | 18 ++++---- .../hexagonal/port/TheatreServicePort.java | 2 +- .../repository/BookingRepository.java | 1 + .../repository/MockBookingRepository.java | 5 +++ .../hexagonal/usecase/BookTicketUseCase.java | 34 ++++++--------- .../RestAPIEndpointAdapterUnitTest.java | 7 ++-- .../usecase/BookTicketUseCaseUnitTest.java | 41 ++++++++----------- 15 files changed, 97 insertions(+), 114 deletions(-) diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java index e6d4dbbe36..1b191bde86 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java @@ -15,4 +15,8 @@ public class BookingPersistenceAdapter implements BookingPersistencePort { public boolean persist(Booking booking) { return bookingRepository.save(booking); } + + public boolean updateStatus(String bookingId, String status) { + return bookingRepository.updateStatus(bookingId, status); + } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java index 7c35f302c0..3e1b1fc90f 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java @@ -2,7 +2,6 @@ package com.baeldung.hexagonal.adapter; import com.baeldung.hexagonal.port.BookingServicePort; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -10,7 +9,9 @@ import org.springframework.web.bind.annotation.RestController; import static com.baeldung.hexagonal.port.BookingServicePort.BookingRequest; import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse; -import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*; +import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.SUCCESS; +import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.FAILED_DEPENDENCY; @RestController public class RestAPIEndpointAdapter { @@ -25,14 +26,7 @@ public class RestAPIEndpointAdapter { @PostMapping(path = "/booking") public ResponseEntity createBooking(@RequestBody BookingRequest request) { BookingResponse response = bookingServicePort.book(request); - - if (response.getStatusCode() == SEAT_NOT_AVAILABLE - || response.getStatusCode() == PAYMENT_FAILED){ - return new ResponseEntity(response, HttpStatus.PRECONDITION_FAILED); - } else if (response.getStatusCode() == UNKNOWN_ERROR) { - return new ResponseEntity(response, HttpStatus.INTERNAL_SERVER_ERROR); - } - - return new ResponseEntity(response, HttpStatus.CREATED); + return new ResponseEntity<>(response, + response.getStatusCode() == SUCCESS ? CREATED : FAILED_DEPENDENCY); } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java index d644f5eee0..fa19483b13 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java @@ -3,29 +3,28 @@ package com.baeldung.hexagonal.adapter; import com.baeldung.hexagonal.external.service.TheatreService; import com.baeldung.hexagonal.port.TheatreServicePort; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import java.util.Optional; import java.util.Set; +import static com.baeldung.hexagonal.external.service.TheatreService.Reservation; +import static org.springframework.http.HttpStatus.CREATED; +import static org.springframework.http.HttpStatus.NO_CONTENT; + @Component public class TheatreServiceAdapter implements TheatreServicePort { @Autowired private TheatreService theatreService; - public Optional reserveSeats(String theatreId, String movieShowId, Set seats) { - ResponseEntity response = theatreService.postReservation(theatreId, movieShowId, seats); - if (response.getStatusCode() == HttpStatus.CREATED) { - return Optional.of(response.getBody().getId()); - } - return Optional.empty(); + public Optional reserveSeats(String movieShowId, Set seats) { + ResponseEntity response = theatreService.postReservation(movieShowId, seats); + return response.getStatusCode() == CREATED ? Optional.of(response.getBody().getId()): Optional.empty(); } public boolean releaseSeats(String resrevationId) { - ResponseEntity response = theatreService.deleteReservation(resrevationId); - return response.getStatusCode() == HttpStatus.NO_CONTENT; + return theatreService.deleteReservation(resrevationId).getStatusCode() == NO_CONTENT; } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java index 930b9c1cf6..1d8db867b7 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java @@ -3,21 +3,17 @@ package com.baeldung.hexagonal.adapter; import com.baeldung.hexagonal.external.service.CustomerWalletService; import com.baeldung.hexagonal.port.WalletServicePort; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; +import static org.springframework.http.HttpStatus.CREATED; + @Component public class WalletServiceAdapter implements WalletServicePort { - private final CustomerWalletService customerWalletService; - @Autowired - public WalletServiceAdapter(CustomerWalletService customerWalletService) { - this.customerWalletService = customerWalletService; - } + private CustomerWalletService customerWalletService; public boolean debit(String customerId, Double amount) { - HttpStatus response = customerWalletService.postDebit(customerId, amount); - return response == HttpStatus.CREATED; + return customerWalletService.postDebit(customerId, amount) == CREATED; } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java b/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java index 34e3f3b81b..ed9b7281f2 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java @@ -1,39 +1,46 @@ package com.baeldung.hexagonal.domain; +import com.baeldung.hexagonal.port.BookingServicePort; + import java.util.Set; +import java.util.UUID; public class Booking { + + public static final String STATUS_INITIAL = "INITIAL"; + public static final String STATUS_SUCCESS = "SUCCESS"; + public static final String STATUS_FAILURE = "FAILED"; + private String bookingId; private String movieShowId; - private String theatreId; private String customerId; private Set seats; private Double amount; - private Status status; - - public enum Status { - INITIAL, SUCCESS, FAILURE - } + private String status; public Booking( - String bookingId, String movieShowId, String theatreId, String customerId, Set seats, Double amount, Status status) { + String bookingId, String movieShowId, String customerId, Set seats, Double amount, String status) { this.bookingId = bookingId; this.movieShowId = movieShowId; - this.theatreId = theatreId; this.customerId = customerId; this.seats = seats; this.amount = amount; - this.status = Status.INITIAL; + this.status = status; + } + + public Booking(BookingServicePort.BookingRequest request) { + this.bookingId = UUID.randomUUID().toString(); + this.movieShowId = request.getMovieShowId(); + this.customerId = request.getCustomerId(); + this.seats = request.getSeats(); + this.amount = request.getAmount(); + this.status = STATUS_INITIAL; } public String getMovieShowId() { return movieShowId; } - public String getTheatreId() { - return theatreId; - } - public Set getSeats() { return seats; } @@ -50,7 +57,7 @@ public class Booking { return amount; } - public Status getStatus() { + public String getStatus() { return status; } @@ -62,10 +69,6 @@ public class Booking { this.movieShowId = movieShowId; } - public void setTheatreId(String theatreId) { - this.theatreId = theatreId; - } - public void setCustomerId(String customerId) { this.customerId = customerId; } @@ -78,7 +81,7 @@ public class Booking { this.amount = amount; } - public void setStatus(Status status) { + public void setStatus(String status) { this.status = status; } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java index 333cbee889..eb018e69b2 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java @@ -10,12 +10,12 @@ import java.util.UUID; @Service public class MockTheatreService implements TheatreService { - public ResponseEntity postReservation(String theatreId, String movieShowId, Set seats) { - return new ResponseEntity( + public ResponseEntity postReservation(String movieShowId, Set seats) { + return new ResponseEntity<>( new Reservation(UUID.randomUUID().toString()), HttpStatus.CREATED); } - public ResponseEntity deleteReservation(String reservationId) { - return new ResponseEntity(HttpStatus.NO_CONTENT); + public ResponseEntity deleteReservation(String reservationId) { + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java index 48f0c5e15d..8107bfb418 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java @@ -6,9 +6,9 @@ import java.util.Set; public interface TheatreService { - ResponseEntity postReservation(String theatreId, String movieShowId, Set seats); + ResponseEntity postReservation(String movieShowId, Set seats); - ResponseEntity deleteReservation(String reservationId); + ResponseEntity deleteReservation(String reservationId); class Reservation { private final String id; diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java index 0deffb42e0..c1d1d73630 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java @@ -4,4 +4,5 @@ import com.baeldung.hexagonal.domain.Booking; public interface BookingPersistencePort { boolean persist(Booking booking); + public boolean updateStatus(String bookingId, String status); } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java index 3b444df4f6..12710a6014 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java @@ -9,7 +9,6 @@ public interface BookingServicePort { class BookingRequest { private String movieShowId; private String customerId; - private String theatreId; private Set seats; private Double amount; @@ -29,14 +28,6 @@ public interface BookingServicePort { this.customerId = customerId; } - public String getTheatreId() { - return theatreId; - } - - public void setTheatreId(String theatreId) { - this.theatreId = theatreId; - } - public Set getSeats() { return seats; } @@ -61,6 +52,12 @@ public interface BookingServicePort { public static final int UNKNOWN_ERROR = 3; private final int statusCode; + private String bookingId; + + public BookingResponse(String bookingId, int statusCode) { + this.bookingId = bookingId; + this.statusCode = statusCode; + } public BookingResponse(int statusCode) { this.statusCode = statusCode; @@ -69,5 +66,8 @@ public interface BookingServicePort { public int getStatusCode() { return statusCode; } + public String getBookingId() { + return bookingId; + } } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java index dac517c724..8e5fab8b46 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java @@ -4,6 +4,6 @@ import java.util.Optional; import java.util.Set; public interface TheatreServicePort { - Optional reserveSeats(String theatreId, String movieShowId, Set seats); + Optional reserveSeats(String movieShowId, Set seats); boolean releaseSeats(String resrevationId); } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java b/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java index c56ffe1308..7dd20290ba 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java @@ -5,4 +5,5 @@ import com.baeldung.hexagonal.domain.Booking; public interface BookingRepository { boolean save(Booking booking); + boolean updateStatus(String bookingId, String status); } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java b/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java index d378cff0af..1738e606d0 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java @@ -8,4 +8,9 @@ public class MockBookingRepository implements BookingRepository { public boolean save(Booking booking) { return true; } + + @Override + public boolean updateStatus(String bookingId, String status) { + return true; + } } diff --git a/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java b/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java index 7d9db9724e..8d3112bd37 100644 --- a/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java +++ b/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java @@ -9,8 +9,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Optional; -import java.util.UUID; +import static com.baeldung.hexagonal.domain.Booking.STATUS_FAILURE; +import static com.baeldung.hexagonal.domain.Booking.STATUS_SUCCESS; import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*; @Component @@ -29,39 +30,28 @@ public class BookTicketUseCase implements BookingServicePort { public BookingResponse book(BookingRequest request) { - Booking booking = new Booking( - UUID.randomUUID().toString(), - request.getMovieShowId(), - request.getTheatreId(), - request.getCustomerId(), - request.getSeats(), - request.getAmount(), - Booking.Status.INITIAL); + Booking booking = new Booking(request); if (!bookingPersistencePort.persist(booking)) { return new BookingResponse(UNKNOWN_ERROR); } - Optional reservationIdOptional = theatreServicePort.reserveSeats( - booking.getTheatreId(), - booking.getMovieShowId(), - booking.getSeats()); + String bookingId = booking.getBookingId(); + + Optional reservationIdOptional = theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats()); if (!reservationIdOptional.isPresent()) { - booking.setStatus(Booking.Status.FAILURE); - bookingPersistencePort.persist(booking); - return new BookingResponse(SEAT_NOT_AVAILABLE); + bookingPersistencePort.updateStatus(bookingId, STATUS_FAILURE); + return new BookingResponse(bookingId, SEAT_NOT_AVAILABLE); } if (!walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) { reservationIdOptional.ifPresent(reservationId -> theatreServicePort.releaseSeats(reservationId)); - booking.setStatus(Booking.Status.FAILURE); - bookingPersistencePort.persist(booking); - return new BookingResponse(PAYMENT_FAILED); + bookingPersistencePort.updateStatus(bookingId, STATUS_FAILURE); + return new BookingResponse(bookingId, PAYMENT_FAILED); } - booking.setStatus(Booking.Status.SUCCESS); - bookingPersistencePort.persist(booking); + bookingPersistencePort.updateStatus(bookingId, STATUS_SUCCESS); - return new BookingResponse(SUCCESS); + return new BookingResponse(bookingId, SUCCESS); } } diff --git a/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java b/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java index 76d853fdda..cb22f5f37a 100644 --- a/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java +++ b/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java @@ -30,7 +30,6 @@ class RestAPIEndpointAdapterUnitTest { private BookingServicePort.BookingRequest getBookingRequest() { BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest(); - request.setTheatreId("theatre-id"); request.setMovieShowId("movie-show-id"); request.setCustomerId("customer-id"); request.setSeats(new HashSet<>(Arrays.asList("A1", "A2"))); @@ -47,7 +46,7 @@ class RestAPIEndpointAdapterUnitTest { verify(bookingServicePort).book(any(BookingRequest.class)); assertNotNull(response); - assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode()); } @Test @@ -59,7 +58,7 @@ class RestAPIEndpointAdapterUnitTest { verify(bookingServicePort).book(any(BookingRequest.class)); assertNotNull(response); - assertEquals(HttpStatus.PRECONDITION_FAILED, response.getStatusCode()); + assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode()); } @Test @@ -71,7 +70,7 @@ class RestAPIEndpointAdapterUnitTest { verify(bookingServicePort).book(any(BookingRequest.class)); assertNotNull(response); - assertEquals(HttpStatus.PRECONDITION_FAILED, response.getStatusCode()); + assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode()); } @Test diff --git a/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java b/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java index eec2b65609..ffb4246f79 100644 --- a/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java +++ b/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java @@ -34,7 +34,6 @@ class BookTicketUseCaseUnitTest { private BookingServicePort.BookingRequest getBookingRequest() { BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest(); - request.setTheatreId("theatre-id"); request.setMovieShowId("movie-show-id"); request.setCustomerId("customer-id"); request.setSeats(new HashSet<>(Arrays.asList("A1", "A2"))); @@ -42,21 +41,10 @@ class BookTicketUseCaseUnitTest { return request; } - private Booking getBooking(BookingServicePort.BookingRequest request) { - return new Booking( - "booking-id", - request.getMovieShowId(), - request.getTheatreId(), - request.getCustomerId(), - request.getSeats(), - request.getAmount(), - Booking.Status.INITIAL); - } - @Test void whenErrorInInitialPersistence_thenReturnUnknownError() { BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = getBooking(request); + Booking booking = new Booking(request); when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(false); BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); @@ -68,14 +56,15 @@ class BookTicketUseCaseUnitTest { @Test void whenErrorInReserveSeats_thenReturnSeatNotAvailable() { BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = getBooking(request); + Booking booking = new Booking(request); when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); - when(theatreServicePort.reserveSeats(booking.getTheatreId(), booking.getMovieShowId(), booking.getSeats())) + when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats())) .thenReturn(Optional.empty()); BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); - verify(bookingPersistencePort, times(2)).persist(any(Booking.class)); - verify(theatreServicePort).reserveSeats(any(String.class), any(String.class), any(HashSet.class)); + verify(bookingPersistencePort).persist(any(Booking.class)); + verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class)); + verify(theatreServicePort).reserveSeats(any(String.class), any(HashSet.class)); assertNotNull(response); assertEquals(BookingServicePort.BookingResponse.SEAT_NOT_AVAILABLE, response.getStatusCode()); } @@ -83,16 +72,17 @@ class BookTicketUseCaseUnitTest { @Test void whenErrorInWalletDebit_thenReturnPaymentFailed() { BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = getBooking(request); + Booking booking = new Booking(request); when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); - when(theatreServicePort.reserveSeats(booking.getTheatreId(), booking.getMovieShowId(), booking.getSeats())) + when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats())) .thenReturn(Optional.of("reservation-id")); when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) .thenReturn(false); BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); - verify(bookingPersistencePort, times(2)).persist(any(Booking.class)); - verify(theatreServicePort).reserveSeats(any(String.class), any(String.class), any(HashSet.class)); + verify(bookingPersistencePort).persist(any(Booking.class)); + verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class)); + verify(theatreServicePort).reserveSeats(any(String.class), any(HashSet.class)); verify(walletServicePort).debit(any(String.class), any(Double.class)); verify(theatreServicePort).releaseSeats(any(String.class)); assertNotNull(response); @@ -102,16 +92,17 @@ class BookTicketUseCaseUnitTest { @Test void whenNoErrorInAnyPorts_thenReturnSuccess() { BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = getBooking(request); + Booking booking = new Booking(request); when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); - when(theatreServicePort.reserveSeats(booking.getTheatreId(), booking.getMovieShowId(), booking.getSeats())) + when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats())) .thenReturn(Optional.of("reservation-id")); when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) .thenReturn(true); BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); - verify(bookingPersistencePort, times(2)).persist(any(Booking.class)); - verify(theatreServicePort).reserveSeats(any(String.class), any(String.class), any(HashSet.class)); + verify(bookingPersistencePort).persist(any(Booking.class)); + verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class)); + verify(theatreServicePort).reserveSeats( any(String.class), any(HashSet.class)); verify(walletServicePort).debit(any(String.class), any(Double.class)); assertNotNull(response); assertEquals(BookingServicePort.BookingResponse.SUCCESS, response.getStatusCode()); From c06df0225cff88ca3902c74f0c37b10602952567 Mon Sep 17 00:00:00 2001 From: Swapan Pramanick Date: Sat, 30 Oct 2021 01:18:37 +0200 Subject: [PATCH 3/7] BAEL-4546: sample app to show-case docker compose with the latest image --- docker/docker-sample-app/Dockerfile | 3 + docker/docker-sample-app/README.md | 3 + .../docker-compose-build-image.yaml | 8 + .../docker-compose-with-image.yaml | 9 + docker/docker-sample-app/mvnw | 310 ++++++++++++++++++ docker/docker-sample-app/mvnw.cmd | 182 ++++++++++ docker/docker-sample-app/pom.xml | 45 +++ .../personal/dockapp/DockAppApplication.java | 13 + .../dockapp/endpoint/MyController.java | 13 + .../src/main/resources/application.properties | 1 + .../dockapp/DockAppApplicationUnitTest.java | 13 + docker/pom.xml | 1 + 12 files changed, 601 insertions(+) create mode 100644 docker/docker-sample-app/Dockerfile create mode 100644 docker/docker-sample-app/README.md create mode 100644 docker/docker-sample-app/docker-compose-build-image.yaml create mode 100644 docker/docker-sample-app/docker-compose-with-image.yaml create mode 100755 docker/docker-sample-app/mvnw create mode 100644 docker/docker-sample-app/mvnw.cmd create mode 100644 docker/docker-sample-app/pom.xml create mode 100644 docker/docker-sample-app/src/main/java/com/personal/dockapp/DockAppApplication.java create mode 100644 docker/docker-sample-app/src/main/java/com/personal/dockapp/endpoint/MyController.java create mode 100644 docker/docker-sample-app/src/main/resources/application.properties create mode 100644 docker/docker-sample-app/src/test/java/com/personal/dockapp/DockAppApplicationUnitTest.java diff --git a/docker/docker-sample-app/Dockerfile b/docker/docker-sample-app/Dockerfile new file mode 100644 index 0000000000..71fc1a29d9 --- /dev/null +++ b/docker/docker-sample-app/Dockerfile @@ -0,0 +1,3 @@ +FROM openjdk:11 +COPY target/docker-sample-app-0.0.1.jar app.jar +ENTRYPOINT ["java","-jar","/app.jar"] diff --git a/docker/docker-sample-app/README.md b/docker/docker-sample-app/README.md new file mode 100644 index 0000000000..6aeaa1d2a3 --- /dev/null +++ b/docker/docker-sample-app/README.md @@ -0,0 +1,3 @@ +### Relevant Articles: + +- How to Get Docker-Compose to Always Use the Latest Image diff --git a/docker/docker-sample-app/docker-compose-build-image.yaml b/docker/docker-sample-app/docker-compose-build-image.yaml new file mode 100644 index 0000000000..27c1d8ee44 --- /dev/null +++ b/docker/docker-sample-app/docker-compose-build-image.yaml @@ -0,0 +1,8 @@ +version: '2.4' +services: + db: + image: postgres + my_app: + build: . + ports: + - "8080:8080" diff --git a/docker/docker-sample-app/docker-compose-with-image.yaml b/docker/docker-sample-app/docker-compose-with-image.yaml new file mode 100644 index 0000000000..9a8822f762 --- /dev/null +++ b/docker/docker-sample-app/docker-compose-with-image.yaml @@ -0,0 +1,9 @@ +version: '2.4' +services: + db: + image: postgres + my_app: + image: "eugen/test-app:latest" + ports: + - "8080:8080" + diff --git a/docker/docker-sample-app/mvnw b/docker/docker-sample-app/mvnw new file mode 100755 index 0000000000..a16b5431b4 --- /dev/null +++ b/docker/docker-sample-app/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/docker/docker-sample-app/mvnw.cmd b/docker/docker-sample-app/mvnw.cmd new file mode 100644 index 0000000000..c8d43372c9 --- /dev/null +++ b/docker/docker-sample-app/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/docker/docker-sample-app/pom.xml b/docker/docker-sample-app/pom.xml new file mode 100644 index 0000000000..416ec45b10 --- /dev/null +++ b/docker/docker-sample-app/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.baeldung.docker + docker + 0.0.1 + + + docker-sample-app + docker-sample-app + Demo project for Spring Boot and Docker + + + 11 + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/docker/docker-sample-app/src/main/java/com/personal/dockapp/DockAppApplication.java b/docker/docker-sample-app/src/main/java/com/personal/dockapp/DockAppApplication.java new file mode 100644 index 0000000000..b0f031d25a --- /dev/null +++ b/docker/docker-sample-app/src/main/java/com/personal/dockapp/DockAppApplication.java @@ -0,0 +1,13 @@ +package com.personal.dockapp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DockAppApplication { + + public static void main(String[] args) { + SpringApplication.run(DockAppApplication.class, args); + } + +} diff --git a/docker/docker-sample-app/src/main/java/com/personal/dockapp/endpoint/MyController.java b/docker/docker-sample-app/src/main/java/com/personal/dockapp/endpoint/MyController.java new file mode 100644 index 0000000000..bf2886f19d --- /dev/null +++ b/docker/docker-sample-app/src/main/java/com/personal/dockapp/endpoint/MyController.java @@ -0,0 +1,13 @@ +package com.personal.dockapp.endpoint; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class MyController { + + @GetMapping + public String version() { + return "1.7"; + } +} diff --git a/docker/docker-sample-app/src/main/resources/application.properties b/docker/docker-sample-app/src/main/resources/application.properties new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/docker/docker-sample-app/src/main/resources/application.properties @@ -0,0 +1 @@ + diff --git a/docker/docker-sample-app/src/test/java/com/personal/dockapp/DockAppApplicationUnitTest.java b/docker/docker-sample-app/src/test/java/com/personal/dockapp/DockAppApplicationUnitTest.java new file mode 100644 index 0000000000..2f8abafe70 --- /dev/null +++ b/docker/docker-sample-app/src/test/java/com/personal/dockapp/DockAppApplicationUnitTest.java @@ -0,0 +1,13 @@ +package com.personal.dockapp; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DockAppApplicationUnitTest { + + @Test + void contextLoads() { + } + +} diff --git a/docker/pom.xml b/docker/pom.xml index 3fcc9ca94f..0a397dd966 100644 --- a/docker/pom.xml +++ b/docker/pom.xml @@ -25,6 +25,7 @@ docker-internal-dto docker-spring-boot + docker-sample-app From 1e9e55daf9adc04fd8f9d959da452017603742fb Mon Sep 17 00:00:00 2001 From: Swapan Pramanick Date: Sat, 30 Oct 2021 01:39:48 +0200 Subject: [PATCH 4/7] BAEL-4546: formatting changes --- .../docker/app}/DockAppApplication.java | 8 ++++---- .../docker/app}/endpoint/MyController.java | 2 +- .../docker/app}/DockAppApplicationUnitTest.java | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) rename docker/docker-sample-app/src/main/java/com/{personal/dockapp => baeldung/docker/app}/DockAppApplication.java (55%) rename docker/docker-sample-app/src/main/java/com/{personal/dockapp => baeldung/docker/app}/endpoint/MyController.java (85%) rename docker/docker-sample-app/src/test/java/com/{personal/dockapp => baeldung/docker/app}/DockAppApplicationUnitTest.java (67%) diff --git a/docker/docker-sample-app/src/main/java/com/personal/dockapp/DockAppApplication.java b/docker/docker-sample-app/src/main/java/com/baeldung/docker/app/DockAppApplication.java similarity index 55% rename from docker/docker-sample-app/src/main/java/com/personal/dockapp/DockAppApplication.java rename to docker/docker-sample-app/src/main/java/com/baeldung/docker/app/DockAppApplication.java index b0f031d25a..e7ff52015c 100644 --- a/docker/docker-sample-app/src/main/java/com/personal/dockapp/DockAppApplication.java +++ b/docker/docker-sample-app/src/main/java/com/baeldung/docker/app/DockAppApplication.java @@ -1,4 +1,4 @@ -package com.personal.dockapp; +package com.baeldung.docker.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -6,8 +6,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DockAppApplication { - public static void main(String[] args) { - SpringApplication.run(DockAppApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(DockAppApplication.class, args); + } } diff --git a/docker/docker-sample-app/src/main/java/com/personal/dockapp/endpoint/MyController.java b/docker/docker-sample-app/src/main/java/com/baeldung/docker/app/endpoint/MyController.java similarity index 85% rename from docker/docker-sample-app/src/main/java/com/personal/dockapp/endpoint/MyController.java rename to docker/docker-sample-app/src/main/java/com/baeldung/docker/app/endpoint/MyController.java index bf2886f19d..d46c57e606 100644 --- a/docker/docker-sample-app/src/main/java/com/personal/dockapp/endpoint/MyController.java +++ b/docker/docker-sample-app/src/main/java/com/baeldung/docker/app/endpoint/MyController.java @@ -1,4 +1,4 @@ -package com.personal.dockapp.endpoint; +package com.baeldung.docker.app.endpoint; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/docker/docker-sample-app/src/test/java/com/personal/dockapp/DockAppApplicationUnitTest.java b/docker/docker-sample-app/src/test/java/com/baeldung/docker/app/DockAppApplicationUnitTest.java similarity index 67% rename from docker/docker-sample-app/src/test/java/com/personal/dockapp/DockAppApplicationUnitTest.java rename to docker/docker-sample-app/src/test/java/com/baeldung/docker/app/DockAppApplicationUnitTest.java index 2f8abafe70..7220766988 100644 --- a/docker/docker-sample-app/src/test/java/com/personal/dockapp/DockAppApplicationUnitTest.java +++ b/docker/docker-sample-app/src/test/java/com/baeldung/docker/app/DockAppApplicationUnitTest.java @@ -1,4 +1,4 @@ -package com.personal.dockapp; +package com.baeldung.docker.app; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -6,8 +6,8 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class DockAppApplicationUnitTest { - @Test - void contextLoads() { - } + @Test + void contextLoads() { + } } From e29cfda933ba0ecbc16d969dba0bb53b9ccd8683 Mon Sep 17 00:00:00 2001 From: Swapan Pramanick Date: Sat, 30 Oct 2021 02:01:47 +0200 Subject: [PATCH 5/7] BAEL-4546: Reverting evaluation article code --- .../hexagonal/HexagonalSpringApplication.java | 20 ---- .../adapter/BookingPersistenceAdapter.java | 22 ---- .../adapter/RestAPIEndpointAdapter.java | 32 ----- .../adapter/TheatreServiceAdapter.java | 30 ----- .../adapter/WalletServiceAdapter.java | 19 --- .../baeldung/hexagonal/domain/Booking.java | 87 -------------- .../service/CustomerWalletService.java | 8 -- .../service/MockCustomerWalletService.java | 11 -- .../external/service/MockTheatreService.java | 21 ---- .../external/service/TheatreService.java | 24 ---- .../port/BookingPersistencePort.java | 8 -- .../hexagonal/port/BookingServicePort.java | 73 ------------ .../hexagonal/port/TheatreServicePort.java | 9 -- .../hexagonal/port/WalletServicePort.java | 5 - .../repository/BookingRepository.java | 9 -- .../repository/MockBookingRepository.java | 16 --- .../hexagonal/usecase/BookTicketUseCase.java | 57 --------- .../RestAPIEndpointAdapterUnitTest.java | 87 -------------- .../usecase/BookTicketUseCaseUnitTest.java | 110 ------------------ 19 files changed, 648 deletions(-) delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java delete mode 100644 ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java delete mode 100644 ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java delete mode 100644 ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java diff --git a/ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java b/ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java deleted file mode 100644 index c679d459f0..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/HexagonalSpringApplication.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.baeldung.hexagonal; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; -import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; -import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; - - -@SpringBootApplication(exclude={ - CassandraAutoConfiguration.class, - MongoDataAutoConfiguration.class, - MongoAutoConfiguration.class -}) -public class HexagonalSpringApplication { - - public static void main(final String[] args) { - SpringApplication.run(HexagonalSpringApplication.class, args); - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java deleted file mode 100644 index 1b191bde86..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/BookingPersistenceAdapter.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.baeldung.hexagonal.adapter; - -import com.baeldung.hexagonal.domain.Booking; -import com.baeldung.hexagonal.port.BookingPersistencePort; -import com.baeldung.hexagonal.repository.BookingRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -@Component -public class BookingPersistenceAdapter implements BookingPersistencePort { - - @Autowired - private BookingRepository bookingRepository; - - public boolean persist(Booking booking) { - return bookingRepository.save(booking); - } - - public boolean updateStatus(String bookingId, String status) { - return bookingRepository.updateStatus(bookingId, status); - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java deleted file mode 100644 index 3e1b1fc90f..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapter.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.baeldung.hexagonal.adapter; - -import com.baeldung.hexagonal.port.BookingServicePort; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -import static com.baeldung.hexagonal.port.BookingServicePort.BookingRequest; -import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse; -import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.SUCCESS; -import static org.springframework.http.HttpStatus.CREATED; -import static org.springframework.http.HttpStatus.FAILED_DEPENDENCY; - -@RestController -public class RestAPIEndpointAdapter { - - private BookingServicePort bookingServicePort; - - @Autowired - public RestAPIEndpointAdapter(BookingServicePort bookingServicePort) { - this.bookingServicePort = bookingServicePort; - } - - @PostMapping(path = "/booking") - public ResponseEntity createBooking(@RequestBody BookingRequest request) { - BookingResponse response = bookingServicePort.book(request); - return new ResponseEntity<>(response, - response.getStatusCode() == SUCCESS ? CREATED : FAILED_DEPENDENCY); - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java deleted file mode 100644 index fa19483b13..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/TheatreServiceAdapter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.baeldung.hexagonal.adapter; - -import com.baeldung.hexagonal.external.service.TheatreService; -import com.baeldung.hexagonal.port.TheatreServicePort; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; - -import java.util.Optional; -import java.util.Set; - -import static com.baeldung.hexagonal.external.service.TheatreService.Reservation; -import static org.springframework.http.HttpStatus.CREATED; -import static org.springframework.http.HttpStatus.NO_CONTENT; - -@Component -public class TheatreServiceAdapter implements TheatreServicePort { - - @Autowired - private TheatreService theatreService; - - public Optional reserveSeats(String movieShowId, Set seats) { - ResponseEntity response = theatreService.postReservation(movieShowId, seats); - return response.getStatusCode() == CREATED ? Optional.of(response.getBody().getId()): Optional.empty(); - } - - public boolean releaseSeats(String resrevationId) { - return theatreService.deleteReservation(resrevationId).getStatusCode() == NO_CONTENT; - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java b/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java deleted file mode 100644 index 1d8db867b7..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/adapter/WalletServiceAdapter.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.baeldung.hexagonal.adapter; - -import com.baeldung.hexagonal.external.service.CustomerWalletService; -import com.baeldung.hexagonal.port.WalletServicePort; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import static org.springframework.http.HttpStatus.CREATED; - -@Component -public class WalletServiceAdapter implements WalletServicePort { - - @Autowired - private CustomerWalletService customerWalletService; - - public boolean debit(String customerId, Double amount) { - return customerWalletService.postDebit(customerId, amount) == CREATED; - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java b/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java deleted file mode 100644 index ed9b7281f2..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/domain/Booking.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.baeldung.hexagonal.domain; - -import com.baeldung.hexagonal.port.BookingServicePort; - -import java.util.Set; -import java.util.UUID; - -public class Booking { - - public static final String STATUS_INITIAL = "INITIAL"; - public static final String STATUS_SUCCESS = "SUCCESS"; - public static final String STATUS_FAILURE = "FAILED"; - - private String bookingId; - private String movieShowId; - private String customerId; - private Set seats; - private Double amount; - private String status; - - public Booking( - String bookingId, String movieShowId, String customerId, Set seats, Double amount, String status) { - this.bookingId = bookingId; - this.movieShowId = movieShowId; - this.customerId = customerId; - this.seats = seats; - this.amount = amount; - this.status = status; - } - - public Booking(BookingServicePort.BookingRequest request) { - this.bookingId = UUID.randomUUID().toString(); - this.movieShowId = request.getMovieShowId(); - this.customerId = request.getCustomerId(); - this.seats = request.getSeats(); - this.amount = request.getAmount(); - this.status = STATUS_INITIAL; - } - - public String getMovieShowId() { - return movieShowId; - } - - public Set getSeats() { - return seats; - } - - public String getCustomerId() { - return customerId; - } - - public String getBookingId() { - return bookingId; - } - - public Double getAmount() { - return amount; - } - - public String getStatus() { - return status; - } - - public void setBookingId(String bookingId) { - this.bookingId = bookingId; - } - - public void setMovieShowId(String movieShowId) { - this.movieShowId = movieShowId; - } - - public void setCustomerId(String customerId) { - this.customerId = customerId; - } - - public void setSeats(Set seats) { - this.seats = seats; - } - - public void setAmount(Double amount) { - this.amount = amount; - } - - public void setStatus(String status) { - this.status = status; - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java deleted file mode 100644 index 102cef788e..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/external/service/CustomerWalletService.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.baeldung.hexagonal.external.service; - -import org.springframework.http.HttpStatus; - -public interface CustomerWalletService { - - HttpStatus postDebit(String customerId, Double amount); -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java deleted file mode 100644 index 4a76368b19..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockCustomerWalletService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.baeldung.hexagonal.external.service; - -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; - -@Service -public class MockCustomerWalletService implements CustomerWalletService { - public HttpStatus postDebit(String customerId, Double amount) { - return HttpStatus.CREATED; - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java deleted file mode 100644 index eb018e69b2..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/external/service/MockTheatreService.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.baeldung.hexagonal.external.service; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; - -import java.util.Set; -import java.util.UUID; - -@Service -public class MockTheatreService implements TheatreService { - - public ResponseEntity postReservation(String movieShowId, Set seats) { - return new ResponseEntity<>( - new Reservation(UUID.randomUUID().toString()), HttpStatus.CREATED); - } - - public ResponseEntity deleteReservation(String reservationId) { - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java b/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java deleted file mode 100644 index 8107bfb418..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/external/service/TheatreService.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.baeldung.hexagonal.external.service; - -import org.springframework.http.ResponseEntity; - -import java.util.Set; - -public interface TheatreService { - - ResponseEntity postReservation(String movieShowId, Set seats); - - ResponseEntity deleteReservation(String reservationId); - - class Reservation { - private final String id; - - public Reservation(String id) { - this.id = id; - } - - public String getId() { - return id; - } - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java deleted file mode 100644 index c1d1d73630..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingPersistencePort.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.baeldung.hexagonal.port; - -import com.baeldung.hexagonal.domain.Booking; - -public interface BookingPersistencePort { - boolean persist(Booking booking); - public boolean updateStatus(String bookingId, String status); -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java deleted file mode 100644 index 12710a6014..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/port/BookingServicePort.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.baeldung.hexagonal.port; - -import java.util.Set; - -public interface BookingServicePort { - - BookingResponse book(BookingRequest request); - - class BookingRequest { - private String movieShowId; - private String customerId; - private Set seats; - private Double amount; - - public String getMovieShowId() { - return movieShowId; - } - - public void setMovieShowId(String movieShowId) { - this.movieShowId = movieShowId; - } - - public String getCustomerId() { - return customerId; - } - - public void setCustomerId(String customerId) { - this.customerId = customerId; - } - - public Set getSeats() { - return seats; - } - - public void setSeats(Set seats) { - this.seats = seats; - } - - public Double getAmount() { - return amount; - } - - public void setAmount(Double amount) { - this.amount = amount; - } - } - - class BookingResponse { - public static final int SUCCESS = 0; - public static final int SEAT_NOT_AVAILABLE = 1; - public static final int PAYMENT_FAILED = 2; - public static final int UNKNOWN_ERROR = 3; - - private final int statusCode; - private String bookingId; - - public BookingResponse(String bookingId, int statusCode) { - this.bookingId = bookingId; - this.statusCode = statusCode; - } - - public BookingResponse(int statusCode) { - this.statusCode = statusCode; - } - - public int getStatusCode() { - return statusCode; - } - public String getBookingId() { - return bookingId; - } - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java deleted file mode 100644 index 8e5fab8b46..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/port/TheatreServicePort.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.baeldung.hexagonal.port; - -import java.util.Optional; -import java.util.Set; - -public interface TheatreServicePort { - Optional reserveSeats(String movieShowId, Set seats); - boolean releaseSeats(String resrevationId); -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java b/ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java deleted file mode 100644 index 102bb619e8..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/port/WalletServicePort.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.baeldung.hexagonal.port; - -public interface WalletServicePort { - boolean debit(String customerId, Double amount); -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java b/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java deleted file mode 100644 index 7dd20290ba..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/repository/BookingRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.baeldung.hexagonal.repository; - -import com.baeldung.hexagonal.domain.Booking; - -public interface BookingRepository { - - boolean save(Booking booking); - boolean updateStatus(String bookingId, String status); -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java b/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java deleted file mode 100644 index 1738e606d0..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/repository/MockBookingRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.baeldung.hexagonal.repository; - -import com.baeldung.hexagonal.domain.Booking; -import org.springframework.stereotype.Repository; - -@Repository -public class MockBookingRepository implements BookingRepository { - public boolean save(Booking booking) { - return true; - } - - @Override - public boolean updateStatus(String bookingId, String status) { - return true; - } -} diff --git a/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java b/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java deleted file mode 100644 index 8d3112bd37..0000000000 --- a/ddd/src/main/java/com/baeldung/hexagonal/usecase/BookTicketUseCase.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.baeldung.hexagonal.usecase; - -import com.baeldung.hexagonal.domain.Booking; -import com.baeldung.hexagonal.port.BookingPersistencePort; -import com.baeldung.hexagonal.port.BookingServicePort; -import com.baeldung.hexagonal.port.TheatreServicePort; -import com.baeldung.hexagonal.port.WalletServicePort; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.Optional; - -import static com.baeldung.hexagonal.domain.Booking.STATUS_FAILURE; -import static com.baeldung.hexagonal.domain.Booking.STATUS_SUCCESS; -import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*; - -@Component -public class BookTicketUseCase implements BookingServicePort { - - private BookingPersistencePort bookingPersistencePort; - private TheatreServicePort theatreServicePort; - private WalletServicePort walletServicePort; - - @Autowired - public BookTicketUseCase(BookingPersistencePort bookingPersistencePort, TheatreServicePort theatreServicePort, WalletServicePort walletServicePort) { - this.bookingPersistencePort = bookingPersistencePort; - this.theatreServicePort = theatreServicePort; - this.walletServicePort = walletServicePort; - } - - public BookingResponse book(BookingRequest request) { - - Booking booking = new Booking(request); - - if (!bookingPersistencePort.persist(booking)) { - return new BookingResponse(UNKNOWN_ERROR); - } - - String bookingId = booking.getBookingId(); - - Optional reservationIdOptional = theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats()); - if (!reservationIdOptional.isPresent()) { - bookingPersistencePort.updateStatus(bookingId, STATUS_FAILURE); - return new BookingResponse(bookingId, SEAT_NOT_AVAILABLE); - } - - if (!walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) { - reservationIdOptional.ifPresent(reservationId -> theatreServicePort.releaseSeats(reservationId)); - bookingPersistencePort.updateStatus(bookingId, STATUS_FAILURE); - return new BookingResponse(bookingId, PAYMENT_FAILED); - } - - bookingPersistencePort.updateStatus(bookingId, STATUS_SUCCESS); - - return new BookingResponse(bookingId, SUCCESS); - } -} diff --git a/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java b/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java deleted file mode 100644 index cb22f5f37a..0000000000 --- a/ddd/src/test/java/com/baeldung/hexagonal/adapter/RestAPIEndpointAdapterUnitTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.baeldung.hexagonal.adapter; - -import com.baeldung.hexagonal.port.BookingServicePort; -import com.baeldung.hexagonal.port.BookingServicePort.BookingRequest; -import com.baeldung.hexagonal.port.BookingServicePort.BookingResponse; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -import java.util.Arrays; -import java.util.HashSet; - -import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -class RestAPIEndpointAdapterUnitTest { - - private BookingServicePort bookingServicePort; - private RestAPIEndpointAdapter restAPIEndpointAdapter; - - @BeforeEach - void setUp() { - bookingServicePort = mock(BookingServicePort.class); - restAPIEndpointAdapter = new RestAPIEndpointAdapter(bookingServicePort); - } - - private BookingServicePort.BookingRequest getBookingRequest() { - BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest(); - request.setMovieShowId("movie-show-id"); - request.setCustomerId("customer-id"); - request.setSeats(new HashSet<>(Arrays.asList("A1", "A2"))); - request.setAmount(100.00); - return request; - } - - @Test - void whenBookingServicePortReturnsUnknownError_thenReturnInternalServerError() { - BookingServicePort.BookingRequest request = getBookingRequest(); - when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(UNKNOWN_ERROR)); - - ResponseEntity response = restAPIEndpointAdapter.createBooking(request); - - verify(bookingServicePort).book(any(BookingRequest.class)); - assertNotNull(response); - assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode()); - } - - @Test - void whenBookingServicePortReturnsSeatUnavailable_thenReturnPreconditionFailed() { - BookingServicePort.BookingRequest request = getBookingRequest(); - when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(SEAT_NOT_AVAILABLE)); - - ResponseEntity response = restAPIEndpointAdapter.createBooking(request); - - verify(bookingServicePort).book(any(BookingRequest.class)); - assertNotNull(response); - assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode()); - } - - @Test - void whenBookingServicePortReturnsPaymentFailed_thenReturnPreconditionFailed() { - BookingServicePort.BookingRequest request = getBookingRequest(); - when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(PAYMENT_FAILED)); - - ResponseEntity response = restAPIEndpointAdapter.createBooking(request); - - verify(bookingServicePort).book(any(BookingRequest.class)); - assertNotNull(response); - assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode()); - } - - @Test - void whenBookingServicePortReturnsSuccess_thenReturnCreated() { - BookingServicePort.BookingRequest request = getBookingRequest(); - when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(SUCCESS)); - - ResponseEntity response = restAPIEndpointAdapter.createBooking(request); - - verify(bookingServicePort).book(any(BookingRequest.class)); - assertNotNull(response); - assertEquals(HttpStatus.CREATED, response.getStatusCode()); - } -} \ No newline at end of file diff --git a/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java b/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java deleted file mode 100644 index ffb4246f79..0000000000 --- a/ddd/src/test/java/com/baeldung/hexagonal/usecase/BookTicketUseCaseUnitTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.baeldung.hexagonal.usecase; - -import com.baeldung.hexagonal.domain.Booking; -import com.baeldung.hexagonal.port.BookingPersistencePort; -import com.baeldung.hexagonal.port.BookingServicePort; -import com.baeldung.hexagonal.port.TheatreServicePort; -import com.baeldung.hexagonal.port.WalletServicePort; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -class BookTicketUseCaseUnitTest { - - private BookingPersistencePort bookingPersistencePort; - private TheatreServicePort theatreServicePort; - private WalletServicePort walletServicePort; - private BookTicketUseCase bookTicketUseCase; - - @BeforeEach - void setUp() { - bookingPersistencePort = mock(BookingPersistencePort.class); - theatreServicePort = mock(TheatreServicePort.class); - walletServicePort = mock(WalletServicePort.class); - bookTicketUseCase = new BookTicketUseCase(bookingPersistencePort, theatreServicePort, walletServicePort); - } - - private BookingServicePort.BookingRequest getBookingRequest() { - BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest(); - request.setMovieShowId("movie-show-id"); - request.setCustomerId("customer-id"); - request.setSeats(new HashSet<>(Arrays.asList("A1", "A2"))); - request.setAmount(100.00); - return request; - } - - @Test - void whenErrorInInitialPersistence_thenReturnUnknownError() { - BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = new Booking(request); - when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(false); - BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); - - verify(bookingPersistencePort, times(1)).persist(any(Booking.class)); - assertNotNull(response); - assertEquals(BookingServicePort.BookingResponse.UNKNOWN_ERROR, response.getStatusCode()); - } - - @Test - void whenErrorInReserveSeats_thenReturnSeatNotAvailable() { - BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = new Booking(request); - when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); - when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats())) - .thenReturn(Optional.empty()); - BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); - - verify(bookingPersistencePort).persist(any(Booking.class)); - verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class)); - verify(theatreServicePort).reserveSeats(any(String.class), any(HashSet.class)); - assertNotNull(response); - assertEquals(BookingServicePort.BookingResponse.SEAT_NOT_AVAILABLE, response.getStatusCode()); - } - - @Test - void whenErrorInWalletDebit_thenReturnPaymentFailed() { - BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = new Booking(request); - when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); - when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats())) - .thenReturn(Optional.of("reservation-id")); - when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) - .thenReturn(false); - BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); - - verify(bookingPersistencePort).persist(any(Booking.class)); - verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class)); - verify(theatreServicePort).reserveSeats(any(String.class), any(HashSet.class)); - verify(walletServicePort).debit(any(String.class), any(Double.class)); - verify(theatreServicePort).releaseSeats(any(String.class)); - assertNotNull(response); - assertEquals(BookingServicePort.BookingResponse.PAYMENT_FAILED, response.getStatusCode()); - } - - @Test - void whenNoErrorInAnyPorts_thenReturnSuccess() { - BookingServicePort.BookingRequest request = getBookingRequest(); - Booking booking = new Booking(request); - when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true); - when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats())) - .thenReturn(Optional.of("reservation-id")); - when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) - .thenReturn(true); - BookingServicePort.BookingResponse response = bookTicketUseCase.book(request); - - verify(bookingPersistencePort).persist(any(Booking.class)); - verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class)); - verify(theatreServicePort).reserveSeats( any(String.class), any(HashSet.class)); - verify(walletServicePort).debit(any(String.class), any(Double.class)); - assertNotNull(response); - assertEquals(BookingServicePort.BookingResponse.SUCCESS, response.getStatusCode()); - } -} \ No newline at end of file From 15cae84104b9f76bbf13be815db4ff3d4e674a43 Mon Sep 17 00:00:00 2001 From: Swapan Pramanick Date: Fri, 5 Nov 2021 13:24:28 +0100 Subject: [PATCH 6/7] BAEL-4546 incorporate thee review comments --- docker/docker-sample-app/pom.xml | 72 ++++++++++++++++---------------- docker/pom.xml | 2 +- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/docker/docker-sample-app/pom.xml b/docker/docker-sample-app/pom.xml index 416ec45b10..6841fabcee 100644 --- a/docker/docker-sample-app/pom.xml +++ b/docker/docker-sample-app/pom.xml @@ -1,45 +1,45 @@ - 4.0.0 - - com.baeldung.docker - docker - 0.0.1 - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + com.baeldung.docker + docker + 0.0.1 + - docker-sample-app - docker-sample-app - Demo project for Spring Boot and Docker + docker-sample-app + docker-sample-app + Demo project for Spring Boot and Docker - - 11 - + + 11 + - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-web - + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + - - org.springframework.boot - spring-boot-starter-test - test - - + + org.springframework.boot + spring-boot-starter-test + test + + - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + + + org.springframework.boot + spring-boot-maven-plugin + + + diff --git a/docker/pom.xml b/docker/pom.xml index 0a397dd966..f481f1b8b7 100644 --- a/docker/pom.xml +++ b/docker/pom.xml @@ -25,7 +25,7 @@ docker-internal-dto docker-spring-boot - docker-sample-app + docker-sample-app From 7502addfafac08d5b86ff5117da77699b7ddbd0d Mon Sep 17 00:00:00 2001 From: Swapan Pramanick Date: Fri, 5 Nov 2021 14:40:16 +0100 Subject: [PATCH 7/7] BAEL-4546 incorporate thee review comments --- docker/docker-sample-app/mvnw | 310 ------------------------------ docker/docker-sample-app/mvnw.cmd | 182 ------------------ 2 files changed, 492 deletions(-) delete mode 100755 docker/docker-sample-app/mvnw delete mode 100644 docker/docker-sample-app/mvnw.cmd diff --git a/docker/docker-sample-app/mvnw b/docker/docker-sample-app/mvnw deleted file mode 100755 index a16b5431b4..0000000000 --- a/docker/docker-sample-app/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/docker/docker-sample-app/mvnw.cmd b/docker/docker-sample-app/mvnw.cmd deleted file mode 100644 index c8d43372c9..0000000000 --- a/docker/docker-sample-app/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE%