Added logging. Small refactoring.

This commit is contained in:
Alexey Zinchenko 2019-05-12 03:44:05 +03:00
parent 6402b8220d
commit ffc5647cf9
16 changed files with 205 additions and 62 deletions

32
pom.xml
View File

@ -26,11 +26,12 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId> <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency> <exclusions>
<dependency> <exclusion>
<groupId>nz.net.ultraq.thymeleaf</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId> <artifactId>spring-boot-starter-logging</artifactId>
<version>2.3.0</version> </exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -42,23 +43,30 @@
<artifactId>spring-boot-devtools</artifactId> <artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.liquibase</groupId> <groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId> <artifactId>liquibase-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version> <version>8.0.16</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.github.javafaker</groupId> <groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId> <artifactId>javafaker</artifactId>

View File

@ -2,6 +2,8 @@ package com.github.prominence.carrepair.controller;
import com.github.prominence.carrepair.model.Client; import com.github.prominence.carrepair.model.Client;
import com.github.prominence.carrepair.service.ClientService; import com.github.prominence.carrepair.service.ClientService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault; import org.springframework.data.web.PageableDefault;
@ -19,6 +21,8 @@ import java.util.Optional;
@RequestMapping("/client") @RequestMapping("/client")
public class ClientController { public class ClientController {
private static final Logger logger = LogManager.getLogger(ClientController.class);
private ClientService clientService; private ClientService clientService;
public ClientController(ClientService clientService) { public ClientController(ClientService clientService) {
@ -28,6 +32,7 @@ public class ClientController {
@GetMapping @GetMapping
public String index(@PageableDefault Pageable pageable, ModelMap modelMap) { public String index(@PageableDefault Pageable pageable, ModelMap modelMap) {
Page<Client> clientList = clientService.findAll(pageable); Page<Client> clientList = clientService.findAll(pageable);
logger.trace("Request to open list page for Clients. Returning {} page.", () -> pageable.getPageNumber() + 1);
modelMap.addAttribute("clientList", clientList.getContent()); modelMap.addAttribute("clientList", clientList.getContent());
modelMap.addAttribute("totalPages", clientList.getTotalPages()); modelMap.addAttribute("totalPages", clientList.getTotalPages());
@ -37,6 +42,7 @@ public class ClientController {
@GetMapping(value = "/create") @GetMapping(value = "/create")
public String create(Model model) { public String create(Model model) {
logger.trace("Request to open create page for Client.");
model.addAttribute("client", new Client()); model.addAttribute("client", new Client());
return "client/edit"; return "client/edit";
@ -44,19 +50,21 @@ public class ClientController {
@GetMapping(value = "/edit/{id}") @GetMapping(value = "/edit/{id}")
public String edit(@PathVariable Long id, Model model) { public String edit(@PathVariable Long id, Model model) {
logger.trace("Request to open edit page for Client[{}].", () -> id);
Optional<Client> clientOptional = clientService.findById(id); Optional<Client> clientOptional = clientService.findById(id);
if (clientOptional.isPresent()) { if (clientOptional.isPresent()) {
model.addAttribute("client", clientOptional.get()); model.addAttribute("client", clientOptional.get());
return "client/edit"; return "client/edit";
} else { } else {
// TODO: need to show warning
return "redirect:/client"; return "redirect:/client";
} }
} }
@PostMapping(value = "/update/{id}") @PostMapping(value = "/update/{id}")
public String update(@Valid Client client, BindingResult bindingResult, @PathVariable long id) { public String update(@Valid Client client, BindingResult bindingResult, @PathVariable long id) {
logger.trace("Request to save {}.", () -> client);
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
logger.trace("{} has validation {} errors and won't be saved.", () -> client, bindingResult::getErrorCount);
client.setId(id); // why should we do this? client.setId(id); // why should we do this?
return "client/edit"; return "client/edit";
} }
@ -67,7 +75,9 @@ public class ClientController {
@PostMapping(value = "/create") @PostMapping(value = "/create")
public String save(@Valid Client client, BindingResult bindingResult) { public String save(@Valid Client client, BindingResult bindingResult) {
logger.trace("Request to create {}.", () -> client);
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
logger.trace("{} has validation {} errors and won't be created.", () -> client, bindingResult::getErrorCount);
return "client/edit"; return "client/edit";
} }
@ -77,8 +87,8 @@ public class ClientController {
@DeleteMapping(value = "/delete/{id}") @DeleteMapping(value = "/delete/{id}")
public ResponseEntity delete(@PathVariable Long id) { public ResponseEntity delete(@PathVariable Long id) {
logger.trace("Request to delete Client[{}].", () -> id);
boolean deleteSuccess = clientService.deleteClientById(id); boolean deleteSuccess = clientService.deleteClientById(id);
if (deleteSuccess) { if (deleteSuccess) {
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} else { } else {

View File

@ -1,14 +1,18 @@
package com.github.prominence.carrepair.controller; package com.github.prominence.carrepair.controller;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
@Controller @Controller
@RequestMapping("/") @RequestMapping("/")
public class HomeController { public class HomeController {
private static final Logger logger = LogManager.getLogger(HomeController.class);
@RequestMapping @RequestMapping
public String index() { public String index() {
return "home"; logger.trace("Request to open home page.");
return "index";
} }
} }

View File

@ -3,6 +3,8 @@ package com.github.prominence.carrepair.controller;
import com.github.prominence.carrepair.enums.OrderStatus; import com.github.prominence.carrepair.enums.OrderStatus;
import com.github.prominence.carrepair.model.Mechanic; import com.github.prominence.carrepair.model.Mechanic;
import com.github.prominence.carrepair.service.MechanicService; import com.github.prominence.carrepair.service.MechanicService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault; import org.springframework.data.web.PageableDefault;
@ -20,6 +22,7 @@ import java.util.Optional;
@Controller @Controller
@RequestMapping("/mechanic") @RequestMapping("/mechanic")
public class MechanicController { public class MechanicController {
private static final Logger logger = LogManager.getLogger(MechanicController.class);
private MechanicService mechanicService; private MechanicService mechanicService;
@ -29,16 +32,18 @@ public class MechanicController {
@GetMapping @GetMapping
public String index(@PageableDefault Pageable pageable, ModelMap modelMap) { public String index(@PageableDefault Pageable pageable, ModelMap modelMap) {
Page<Mechanic> clientList = mechanicService.findAll(pageable); Page<Mechanic> mechanicList = mechanicService.findAll(pageable);
logger.trace("Request to open list page for Mechanics. Returning {} page.", () -> pageable.getPageNumber() + 1);
modelMap.addAttribute("mechanicList", clientList.getContent()); modelMap.addAttribute("mechanicList", mechanicList.getContent());
modelMap.addAttribute("totalPages", clientList.getTotalPages()); modelMap.addAttribute("totalPages", mechanicList.getTotalPages());
return "mechanic/index"; return "mechanic/index";
} }
@GetMapping(value = "/create") @GetMapping(value = "/create")
public String create(Model model) { public String create(Model model) {
logger.trace("Request to open create page for Mechanic.");
model.addAttribute("mechanic", new Mechanic()); model.addAttribute("mechanic", new Mechanic());
return "mechanic/edit"; return "mechanic/edit";
@ -46,41 +51,45 @@ public class MechanicController {
@GetMapping(value = "/edit/{id}") @GetMapping(value = "/edit/{id}")
public String edit(@PathVariable Long id, Model model) { public String edit(@PathVariable Long id, Model model) {
Optional<Mechanic> clientOptional = mechanicService.findById(id); logger.trace("Request to open edit page for Mechanic[{}].", () -> id);
if (clientOptional.isPresent()) { Optional<Mechanic> mechanicOptional = mechanicService.findById(id);
model.addAttribute("mechanic", clientOptional.get()); if (mechanicOptional.isPresent()) {
model.addAttribute("mechanic", mechanicOptional.get());
return "mechanic/edit"; return "mechanic/edit";
} else { } else {
// TODO: need to show warning
return "redirect:/mechanic"; return "redirect:/mechanic";
} }
} }
@PostMapping(value = "/update/{id}") @PostMapping(value = "/update/{id}")
public String update(@Valid Mechanic client, BindingResult bindingResult, @PathVariable long id) { public String update(@Valid Mechanic mechanic, BindingResult bindingResult, @PathVariable long id) {
logger.trace("Request to save {}.", () -> mechanic);
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
client.setId(id); // why should we do this? logger.trace("{} has validation {} errors and won't be saved.", () -> mechanic, bindingResult::getErrorCount);
mechanic.setId(id); // why should we do this?
return "mechanic/edit"; return "mechanic/edit";
} }
mechanicService.save(client); mechanicService.save(mechanic);
return "redirect:/mechanic"; return "redirect:/mechanic";
} }
@PostMapping(value = "/create") @PostMapping(value = "/create")
public String save(@Valid Mechanic client, BindingResult bindingResult) { public String save(@Valid Mechanic mechanic, BindingResult bindingResult) {
logger.trace("Request to create {}.", () -> mechanic);
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
logger.trace("{} has validation {} errors and won't be created.", () -> mechanic, bindingResult::getErrorCount);
return "mechanic/edit"; return "mechanic/edit";
} }
mechanicService.save(client); mechanicService.save(mechanic);
return "redirect:/mechanic"; return "redirect:/mechanic";
} }
@DeleteMapping(value = "/delete/{id}") @DeleteMapping(value = "/delete/{id}")
public ResponseEntity delete(@PathVariable Long id) { public ResponseEntity delete(@PathVariable Long id) {
logger.trace("Request to delete Mechanic[{}].", () -> id);
boolean deleteSuccess = mechanicService.deleteMechanicById(id); boolean deleteSuccess = mechanicService.deleteMechanicById(id);
if (deleteSuccess) { if (deleteSuccess) {
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} else { } else {
@ -90,6 +99,7 @@ public class MechanicController {
@GetMapping(value = "/statistics/{id}") @GetMapping(value = "/statistics/{id}")
public String statistics(@PathVariable Long id, Model model) { public String statistics(@PathVariable Long id, Model model) {
logger.trace("Request to open statistics for Mechanic[{}].", () -> id);
Map<OrderStatus, Integer> mechanicStatistics = mechanicService.getOrderStatistics(id); Map<OrderStatus, Integer> mechanicStatistics = mechanicService.getOrderStatistics(id);
model.addAttribute("statistics", mechanicStatistics); model.addAttribute("statistics", mechanicStatistics);

View File

@ -4,6 +4,8 @@ import com.github.prominence.carrepair.enums.OrderStatus;
import com.github.prominence.carrepair.model.Order; import com.github.prominence.carrepair.model.Order;
import com.github.prominence.carrepair.repository.spec.OrderSpecifications; import com.github.prominence.carrepair.repository.spec.OrderSpecifications;
import com.github.prominence.carrepair.service.OrderService; import com.github.prominence.carrepair.service.OrderService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault; import org.springframework.data.web.PageableDefault;
@ -19,6 +21,7 @@ import java.util.Optional;
@Controller @Controller
@RequestMapping(value = "/order") @RequestMapping(value = "/order")
public class OrderController { public class OrderController {
private static final Logger logger = LogManager.getLogger(OrderController.class);
private OrderService orderService; private OrderService orderService;
@ -30,6 +33,7 @@ public class OrderController {
public String index(@PageableDefault Pageable pageable, @RequestParam(required = false) String client, @RequestParam(required = false) String description, public String index(@PageableDefault Pageable pageable, @RequestParam(required = false) String client, @RequestParam(required = false) String description,
@RequestParam(required = false) OrderStatus orderStatus, ModelMap modelMap) { @RequestParam(required = false) OrderStatus orderStatus, ModelMap modelMap) {
Page<Order> orderList = orderService.findAll(OrderSpecifications.search(client, description, orderStatus), pageable); Page<Order> orderList = orderService.findAll(OrderSpecifications.search(client, description, orderStatus), pageable);
logger.trace("Request to open list page for Orders. Returning {} page.", () -> pageable.getPageNumber() + 1);
modelMap.addAttribute("orderList", orderList.getContent()); modelMap.addAttribute("orderList", orderList.getContent());
modelMap.addAttribute("totalPages", orderList.getTotalPages()); modelMap.addAttribute("totalPages", orderList.getTotalPages());
@ -40,6 +44,7 @@ public class OrderController {
@GetMapping(value = "/create") @GetMapping(value = "/create")
public String create(Model model) { public String create(Model model) {
logger.trace("Request to open create page for Order.");
model.addAttribute("order", new Order()); model.addAttribute("order", new Order());
model.addAttribute("orderStatuses", OrderStatus.values()); model.addAttribute("orderStatuses", OrderStatus.values());
@ -48,21 +53,23 @@ public class OrderController {
@GetMapping(value = "/edit/{id}") @GetMapping(value = "/edit/{id}")
public String edit(@PathVariable Long id, Model model) { public String edit(@PathVariable Long id, Model model) {
logger.trace("Request to open edit page for Order[{}].", () -> id);
Optional<Order> orderOptional = orderService.findById(id); Optional<Order> orderOptional = orderService.findById(id);
if (orderOptional.isPresent()) { if (orderOptional.isPresent()) {
model.addAttribute("order", orderOptional.get()); model.addAttribute("order", orderOptional.get());
model.addAttribute("orderStatuses", OrderStatus.values()); model.addAttribute("orderStatuses", OrderStatus.values());
return "order/edit"; return "order/edit";
} else { } else {
// TODO: need to show warning
return "redirect:/order"; return "redirect:/order";
} }
} }
@PostMapping(value = "/update/{id}") @PostMapping(value = "/update/{id}")
public String update(Order order, BindingResult bindingResult, @PathVariable long id, Long clientId, Long mechanicId, Model model) { public String update(Order order, BindingResult bindingResult, @PathVariable long id, Long clientId, Long mechanicId, Model model) {
logger.trace("Request to save {}.", () -> order);
orderService.fetchNestedObjectsAndValidate(order, clientId, mechanicId, bindingResult); orderService.fetchNestedObjectsAndValidate(order, clientId, mechanicId, bindingResult);
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
logger.trace("{} has validation {} errors and won't be saved.", () -> order, bindingResult::getErrorCount);
order.setId(id); // why should we do this? order.setId(id); // why should we do this?
model.addAttribute("orderStatuses", OrderStatus.values()); model.addAttribute("orderStatuses", OrderStatus.values());
return "order/edit"; return "order/edit";
@ -74,8 +81,10 @@ public class OrderController {
@PostMapping(value = "/create") @PostMapping(value = "/create")
public String save(Order order, BindingResult bindingResult, Long clientId, Long mechanicId, Model model) { public String save(Order order, BindingResult bindingResult, Long clientId, Long mechanicId, Model model) {
logger.trace("Request to create {}.", () -> order);
orderService.fetchNestedObjectsAndValidate(order, clientId, mechanicId, bindingResult); orderService.fetchNestedObjectsAndValidate(order, clientId, mechanicId, bindingResult);
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
logger.trace("{} has validation {} errors and won't be created.", () -> order, bindingResult::getErrorCount);
model.addAttribute("orderStatuses", OrderStatus.values()); model.addAttribute("orderStatuses", OrderStatus.values());
return "order/edit"; return "order/edit";
} }
@ -86,8 +95,8 @@ public class OrderController {
@DeleteMapping(value = "/delete/{id}") @DeleteMapping(value = "/delete/{id}")
public ResponseEntity delete(@PathVariable Long id) { public ResponseEntity delete(@PathVariable Long id) {
logger.trace("Request to delete Order[{}].", () -> id);
boolean deleteSuccess = orderService.deleteOrderById(id); boolean deleteSuccess = orderService.deleteOrderById(id);
if (deleteSuccess) { if (deleteSuccess) {
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} else { } else {

View File

@ -9,6 +9,8 @@ import com.github.prominence.carrepair.service.ClientService;
import com.github.prominence.carrepair.service.MechanicService; import com.github.prominence.carrepair.service.MechanicService;
import com.github.prominence.carrepair.service.OrderService; import com.github.prominence.carrepair.service.OrderService;
import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.RandomUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@ -26,8 +28,10 @@ import java.util.stream.Stream;
@Component @Component
public class DemoDataPopulator { public class DemoDataPopulator {
private static final int COUNT = 100; private static final int COUNT = 100;
private static final int DEFAULT_PAGE_SIZE = 10;
private final Logger logger = LogManager.getLogger(DemoDataPopulator.class);
private Faker faker = new Faker(); private final Faker faker = new Faker();
@Bean @Bean
public CommandLineRunner clientDemoData(ClientService clientService) { public CommandLineRunner clientDemoData(ClientService clientService) {
@ -37,9 +41,9 @@ public class DemoDataPopulator {
client.setLastName(faker.name().lastName()); client.setLastName(faker.name().lastName());
client.setMiddleName(faker.name().username()); client.setMiddleName(faker.name().username());
client.setPhoneNo(faker.phoneNumber().phoneNumber()); client.setPhoneNo(faker.phoneNumber().phoneNumber());
System.out.println(client); // demo output
return client; return client;
}).limit(COUNT).collect(Collectors.toList()); }).limit(COUNT).collect(Collectors.toList());
logger.info("[Demo Data] Populated {} clients.", demoClientList::size);
return args -> demoClientList.forEach(clientService::save); return args -> demoClientList.forEach(clientService::save);
} }
@ -52,9 +56,9 @@ public class DemoDataPopulator {
mechanic.setLastName(faker.name().lastName()); mechanic.setLastName(faker.name().lastName());
mechanic.setMiddleName(faker.name().username()); mechanic.setMiddleName(faker.name().username());
mechanic.setHourlyPayment(BigDecimal.valueOf(faker.number().randomDouble(3, 100, 999))); mechanic.setHourlyPayment(BigDecimal.valueOf(faker.number().randomDouble(3, 100, 999)));
System.out.println(mechanic); // demo output
return mechanic; return mechanic;
}).limit(COUNT).collect(Collectors.toList()); }).limit(COUNT).collect(Collectors.toList());
logger.info("[Demo Data] Populated {} mechanics.", demoMechanicList::size);
return args -> demoMechanicList.forEach(mechanicService::save); return args -> demoMechanicList.forEach(mechanicService::save);
} }
@ -80,22 +84,22 @@ public class DemoDataPopulator {
final Client randomClient = clientListSlice.get(RandomUtils.nextInt(0, clientListSlice.size())); final Client randomClient = clientListSlice.get(RandomUtils.nextInt(0, clientListSlice.size()));
order.setClient(randomClient); order.setClient(randomClient);
final List<Mechanic> mechanicListSlise = mechanicService.findAll(getRandomPageable(mechanicsCount)).getContent(); final List<Mechanic> mechanicListSlice = mechanicService.findAll(getRandomPageable(mechanicsCount)).getContent();
final Mechanic randomMechanic = mechanicListSlise.get(RandomUtils.nextInt(0, mechanicListSlise.size())); final Mechanic randomMechanic = mechanicListSlice.get(RandomUtils.nextInt(0, mechanicListSlice.size()));
order.setMechanic(randomMechanic); order.setMechanic(randomMechanic);
System.out.println(order); // demo output
return order; return order;
}).limit(COUNT).collect(Collectors.toList()); }).limit(COUNT).collect(Collectors.toList());
logger.info("[Demo Data] Populated {} orders.", demoOrderList::size);
return args -> demoOrderList.forEach(orderService::save); return args -> demoOrderList.forEach(orderService::save);
} }
private Pageable getRandomPageable(long totalRecords) { private Pageable getRandomPageable(long totalRecords) {
final int size = 10; final int totalPages = (int) (totalRecords / DEFAULT_PAGE_SIZE);
final int totalPages = (int) (totalRecords / size); final PageRequest pageRequest = PageRequest.of(RandomUtils.nextInt(0, totalPages), DEFAULT_PAGE_SIZE);
logger.trace("[Demo Data] Random page: {}", () -> pageRequest);
return PageRequest.of(RandomUtils.nextInt(0, totalPages), size); return pageRequest;
} }
} }

View File

@ -1,5 +1,7 @@
package com.github.prominence.carrepair.formatter; package com.github.prominence.carrepair.formatter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.format.Formatter; import org.springframework.format.Formatter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -10,13 +12,19 @@ import java.util.Locale;
@Component @Component
public class CustomDateTimeFormatter implements Formatter<LocalDateTime> { public class CustomDateTimeFormatter implements Formatter<LocalDateTime> {
private static final Logger logger = LogManager.getLogger(CustomDateTimeFormatter.class);
@Override @Override
public LocalDateTime parse(String text, Locale locale) throws ParseException { public LocalDateTime parse(String text, Locale locale) throws ParseException {
return LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); final LocalDateTime parsedDateTime = LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
logger.trace("Parsing String[{}] to LocalDateTime instance: {}.", () -> text, () -> parsedDateTime);
return parsedDateTime;
} }
@Override @Override
public String print(LocalDateTime object, Locale locale) { public String print(LocalDateTime object, Locale locale) {
return object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); final String formattedString = object.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
logger.trace("Formatting LocalDateTime[{}] to String instance: {}.", () -> object, () -> formattedString);
return formattedString;
} }
} }

View File

@ -2,6 +2,8 @@ package com.github.prominence.carrepair.formatter;
import com.github.prominence.carrepair.enums.OrderStatus; import com.github.prominence.carrepair.enums.OrderStatus;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.format.Formatter; import org.springframework.format.Formatter;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -10,13 +12,19 @@ import java.util.Locale;
@Component @Component
public class OrderStatusFormatter implements Formatter<OrderStatus> { public class OrderStatusFormatter implements Formatter<OrderStatus> {
private static final Logger logger = LogManager.getLogger(OrderStatusFormatter.class);
@Override @Override
public OrderStatus parse(String text, Locale locale) throws ParseException { public OrderStatus parse(String text, Locale locale) throws ParseException {
return OrderStatus.valueOf(text.toUpperCase()); final OrderStatus parsedOrderStatus = OrderStatus.valueOf(text.toUpperCase());
logger.trace("Parsing String[{}] to OrderStatus instance: {}.", () -> text, () -> parsedOrderStatus);
return parsedOrderStatus;
} }
@Override @Override
public String print(OrderStatus object, Locale locale) { public String print(OrderStatus object, Locale locale) {
return StringUtils.capitalize(object.toString().toLowerCase()); final String formattedOrderStatus = StringUtils.capitalize(object.toString().toLowerCase());
logger.trace("Formatting OrderStatus[{}] to String instance: {}.", () -> object, () -> formattedOrderStatus);
return formattedOrderStatus;
} }
} }

View File

@ -4,6 +4,8 @@ import com.github.prominence.carrepair.enums.OrderStatus;
import com.github.prominence.carrepair.model.Client; import com.github.prominence.carrepair.model.Client;
import com.github.prominence.carrepair.model.Order; import com.github.prominence.carrepair.model.Order;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -13,8 +15,11 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class OrderSpecifications { public class OrderSpecifications {
private static final Logger logger = LogManager.getLogger(OrderSpecifications.class);
public static Specification<Order> search(@Nullable String clientQuery, @Nullable String orderDescriptionQuery, @Nullable OrderStatus orderStatusQuery) { public static Specification<Order> search(@Nullable String clientQuery, @Nullable String orderDescriptionQuery, @Nullable OrderStatus orderStatusQuery) {
logger.debug("Creating specification for order search. Params: clientQuery=[{}], orderDescriptionQuery=[{}], orderStatusQuery=[{}]",
() -> clientQuery, () -> orderDescriptionQuery, () -> orderStatusQuery);
return (root, criteriaQuery, criteriaBuilder) -> { return (root, criteriaQuery, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();
if (!StringUtils.isEmpty(clientQuery)) { if (!StringUtils.isEmpty(clientQuery)) {

View File

@ -2,6 +2,8 @@ package com.github.prominence.carrepair.service;
import com.github.prominence.carrepair.model.Client; import com.github.prominence.carrepair.model.Client;
import com.github.prominence.carrepair.repository.ClientRepository; import com.github.prominence.carrepair.repository.ClientRepository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -11,6 +13,7 @@ import java.util.Optional;
@Service @Service
public class ClientService { public class ClientService {
private static final Logger logger = LogManager.getLogger(ClientService.class);
private ClientRepository clientRepository; private ClientRepository clientRepository;
@ -19,31 +22,43 @@ public class ClientService {
} }
public Page<Client> findAll(Pageable pageable) { public Page<Client> findAll(Pageable pageable) {
return clientRepository.findAll(pageable); final Page<Client> clientPage = clientRepository.findAll(pageable);
logger.trace(clientPage);
return clientPage;
} }
public Optional<Client> findById(Long id) { public Optional<Client> findById(Long id) {
return clientRepository.findById(id); final Optional<Client> clientOptional = clientRepository.findById(id);
logger.debug("{} found by id={}", () -> clientOptional, () -> id);
return clientOptional;
} }
public Client save(Client client) { public Client save(Client client) {
return clientRepository.save(client); final Client clientToSave = clientRepository.save(client);
logger.trace("[{}] was saved.", () -> clientToSave);
return clientToSave;
} }
public boolean deleteClientById(Long id) { public boolean deleteClientById(Long id) {
try { try {
clientRepository.deleteById(id); clientRepository.deleteById(id);
logger.debug("Client[id={}] was deleted.", () -> id);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
logger.error("Client[id={}] wasn't deleted. Exception: {}", () -> id, e::getMessage);
return false; return false;
} }
} }
public long getClientCount() { public long getClientCount() {
return clientRepository.count(); final long clientCount = clientRepository.count();
logger.trace("Found {} clients.", () -> clientCount);
return clientCount;
} }
public List<Client> searchByInitials(String query) { public List<Client> searchByInitials(String query) {
return clientRepository.findAllByInitials(query); final List<Client> allByInitials = clientRepository.findAllByInitials(query);
logger.debug("Found {} clients by initials: {}.", allByInitials::size, () -> query);
return allByInitials;
} }
} }

View File

@ -5,6 +5,8 @@ import com.github.prominence.carrepair.model.Mechanic;
import com.github.prominence.carrepair.model.Order; import com.github.prominence.carrepair.model.Order;
import com.github.prominence.carrepair.repository.MechanicRepository; import com.github.prominence.carrepair.repository.MechanicRepository;
import com.github.prominence.carrepair.repository.OrderRepository; import com.github.prominence.carrepair.repository.OrderRepository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -16,6 +18,7 @@ import java.util.Optional;
@Service @Service
public class MechanicService { public class MechanicService {
private static final Logger logger = LogManager.getLogger(MechanicService.class);
private MechanicRepository mechanicRepository; private MechanicRepository mechanicRepository;
private OrderRepository orderRepository; private OrderRepository orderRepository;
@ -26,22 +29,30 @@ public class MechanicService {
} }
public Page<Mechanic> findAll(Pageable pageable) { public Page<Mechanic> findAll(Pageable pageable) {
return mechanicRepository.findAll(pageable); final Page<Mechanic> mechanicPage = mechanicRepository.findAll(pageable);
logger.trace(mechanicPage);
return mechanicPage;
} }
public Optional<Mechanic> findById(Long id) { public Optional<Mechanic> findById(Long id) {
return mechanicRepository.findById(id); final Optional<Mechanic> mechanicOptional = mechanicRepository.findById(id);
logger.debug("{} found by id={}", () -> mechanicOptional, () -> id);
return mechanicOptional;
} }
public Mechanic save(Mechanic client) { public Mechanic save(Mechanic client) {
return mechanicRepository.save(client); final Mechanic mechanicToSave = mechanicRepository.save(client);
logger.trace("[{}] was saved.", () -> mechanicToSave);
return mechanicToSave;
} }
public boolean deleteMechanicById(Long id) { public boolean deleteMechanicById(Long id) {
try { try {
mechanicRepository.deleteById(id); mechanicRepository.deleteById(id);
logger.debug("Mechanic[id={}] was deleted.", () -> id);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
logger.error("Mechanic[id={}] wasn't deleted. Exception: {}", () -> id, e::getMessage);
return false; return false;
} }
} }
@ -55,15 +66,20 @@ public class MechanicService {
List<Order> mechanicOrders = orderRepository.findAllByMechanic_Id(mechanicId); List<Order> mechanicOrders = orderRepository.findAllByMechanic_Id(mechanicId);
mechanicOrders.forEach(order -> statistics.merge(order.getOrderStatus(), 1, Integer::sum)); mechanicOrders.forEach(order -> statistics.merge(order.getOrderStatus(), 1, Integer::sum));
logger.trace("Mechanic statistics by ID={}: {}", () -> mechanicId, () -> statistics);
return statistics; return statistics;
} }
public long getMechanicCount() { public long getMechanicCount() {
return mechanicRepository.count(); final long mechanicCount = mechanicRepository.count();
logger.trace("Found {} mechanics.", () -> mechanicCount);
return mechanicCount;
} }
public List<Mechanic> searchByInitials(String query) { public List<Mechanic> searchByInitials(String query) {
return mechanicRepository.findAllByInitials(query); final List<Mechanic> allByInitials = mechanicRepository.findAllByInitials(query);
logger.debug("Found {} mechanics by initials: {}.", allByInitials::size, () -> query);
return allByInitials;
} }
} }

View File

@ -3,6 +3,8 @@ package com.github.prominence.carrepair.service;
import com.github.prominence.carrepair.model.Order; import com.github.prominence.carrepair.model.Order;
import com.github.prominence.carrepair.repository.OrderRepository; import com.github.prominence.carrepair.repository.OrderRepository;
import com.github.prominence.carrepair.validation.OrderValidator; import com.github.prominence.carrepair.validation.OrderValidator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
@ -14,6 +16,7 @@ import java.util.Optional;
@Service @Service
public class OrderService { public class OrderService {
private static final Logger logger = LogManager.getLogger(OrderService.class);
private OrderRepository orderRepository; private OrderRepository orderRepository;
@ -31,35 +34,47 @@ public class OrderService {
} }
public Page<Order> findAll(Specification<Order> specification, Pageable pageable) { public Page<Order> findAll(Specification<Order> specification, Pageable pageable) {
return orderRepository.findAll(specification, pageable); final Page<Order> orderPage = orderRepository.findAll(specification, pageable);
logger.trace(orderPage);
return orderPage;
} }
public Optional<Order> findById(Long id) { public Optional<Order> findById(Long id) {
return orderRepository.findById(id); final Optional<Order> orderOptional = orderRepository.findById(id);
logger.debug("{} found by id={}", () -> orderOptional, () -> id);
return orderOptional;
} }
public Order save(Order client) { public Order save(Order order) {
return orderRepository.save(client); final Order orderToSave = orderRepository.save(order);
logger.trace("[{}] was saved.", () -> orderToSave);
return orderToSave;
} }
public boolean deleteOrderById(Long id) { public boolean deleteOrderById(Long id) {
try { try {
orderRepository.deleteById(id); orderRepository.deleteById(id);
logger.debug("Order[id={}] was deleted.", () -> id);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
logger.error("Order[id={}] wasn't deleted. Exception: {}", () -> id, e::getMessage);
return false; return false;
} }
} }
public long getOrderCount() { public long getOrderCount() {
return orderRepository.count(); final long orderCount = orderRepository.count();
logger.trace("Found {} orders.", () -> orderCount);
return orderCount;
} }
public void fetchNestedObjectsAndValidate(Order order, Long clientId, Long mechanicId, BindingResult bindingResult) { public void fetchNestedObjectsAndValidate(Order order, Long clientId, Long mechanicId, BindingResult bindingResult) {
if (clientId != null) { if (clientId != null) {
logger.trace("Fetching Client[{}] for {}.", () -> clientId, () -> order);
clientService.findById(clientId).ifPresent(order::setClient); clientService.findById(clientId).ifPresent(order::setClient);
} }
if (mechanicId != null) { if (mechanicId != null) {
logger.trace("Fetching Mechanic[{}] for {}.", () -> clientId, () -> order);
mechanicService.findById(mechanicId).ifPresent(order::setMechanic); mechanicService.findById(mechanicId).ifPresent(order::setMechanic);
} }
smartValidator.validate(order, bindingResult); smartValidator.validate(order, bindingResult);

View File

@ -2,6 +2,8 @@ package com.github.prominence.carrepair.validation;
import com.github.prominence.carrepair.enums.OrderStatus; import com.github.prominence.carrepair.enums.OrderStatus;
import com.github.prominence.carrepair.model.Order; import com.github.prominence.carrepair.model.Order;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.validation.Errors; import org.springframework.validation.Errors;
import org.springframework.validation.Validator; import org.springframework.validation.Validator;
@ -10,6 +12,8 @@ import java.time.LocalDateTime;
@Component @Component
public class OrderValidator implements Validator { public class OrderValidator implements Validator {
private static final Logger logger = LogManager.getLogger(OrderValidator.class);
@Override @Override
public boolean supports(Class<?> aClass) { public boolean supports(Class<?> aClass) {
return Order.class.isAssignableFrom(aClass); return Order.class.isAssignableFrom(aClass);
@ -20,12 +24,16 @@ public class OrderValidator implements Validator {
Order order = (Order) o; Order order = (Order) o;
LocalDateTime finishedOn = order.getFinishedOn(); LocalDateTime finishedOn = order.getFinishedOn();
if (finishedOn != null) { if (finishedOn != null) {
LocalDateTime startedOn = order.getCreatedOn(); LocalDateTime createdOn = order.getCreatedOn();
if (startedOn != null && order.getFinishedOn().isBefore(order.getCreatedOn())) { if (createdOn != null && order.getFinishedOn().isBefore(order.getCreatedOn())) {
logger.debug("[{}] validation error: \"finishedOn\" value[{}] is before \"createdOn\"[{}].",
() -> order, () -> finishedOn, () -> createdOn);
errors.rejectValue("finishedOn", "error.finishedOn.finishedBeforeStarted"); errors.rejectValue("finishedOn", "error.finishedOn.finishedBeforeStarted");
} }
if (order.getOrderStatus() != OrderStatus.ACCEPTED) { if (order.getOrderStatus() != OrderStatus.ACCEPTED) {
logger.debug("[{}] validation error: \"finishedOn\" cannot be set for order in status differ from {}.",
() -> order, () -> OrderStatus.ACCEPTED);
errors.rejectValue("finishedOn", "error.finishedOn.incompatibleStatus"); errors.rejectValue("finishedOn", "error.finishedOn.incompatibleStatus");
} }
} }

View File

@ -11,7 +11,7 @@ spring.datasource.password=carrepair
# JPA / HIBERNATE # JPA / HIBERNATE
# =============================== # ===============================
spring.jpa.show-sql=true spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=validate spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect

View File

@ -0,0 +1,23 @@
name = LoggingsConfig
property.filename = logs
appenders = console, file
appender.console.type = Console
appender.console.name = STDOUT
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
appender.file.type = File
appender.file.name = LOGFILE
appender.file.fileName=${filename}/application.log
appender.file.layout.type=PatternLayout
appender.file.layout.pattern=[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
loggers = base
logger.base.name = com.github.prominence
logger.base.level = DEBUG
rootLogger.level = info
rootLogger.appenderRefs = stdout
rootLogger.appenderRef.stdout.ref = STDOUT